C言語の愉快な標準関数たち
2012年9月23日 お仕事ちょっとC言語学んだことがある人向けの、ちょっとテクニカルなことができるようになる技術解説。
解説するにあたってひとつ面白い標準関数があるので、これを使って紹介したいと思う。
それはずばり、qsort。その名のとおり、ある配列に対してソートを行ってくれる関数である。
まずこの関数の定義
void qsort(
void *base,
size_t num,
size_t size,
int (*compare)(const void*, const void*)
)
引数は合計4つ、なかなか愉快な表現になっています。
順番に見ていくと
1、void *base
void *で定義された引数。void *って何やねん、と思うけれども、そもそも
ポインタの教え方がよろしくないと思う。
*hogehoge(変数名)というのは、すべからくアドレスのみを表している。それがどんなものであれ、hogehogeの中身は4バイトのアドレスを指し示すものである。
では、その前についてるintとかfloatとかの型が何をさしているのかというと、そのデータがどこまで入っているのか、ということである。
つまり *hogehogeがデータの開始位置、その前の型データがその開始位置からどこまでデータが入っているのか、という長さを表している。
この場合のvoidは不定を表している。
つまりqsortは長さが不明だけれど、アドレスの値を受け取っている。というわけ。
当然、そのままではデータは取り出せない、どこからどこまでデータなのかが分からないので、参照ができない。
そこで、出てくるのが2番目と3番目の引数。
2番目の引数は、配列の長さで、三番目の引数が変数の長さ。この変数の長さ、というのが型情報に相当するもので、2番目の値は配列の長さである。
そして極め付けが4番目の引数。C勉強してる人でも、ここはすっとばして教えることが多いので分からないかもしれない。
これは、関数ポインタと呼ばれるもの。
プログラムがメモリ上に配置される。
つまり、プログラムコード自身もアドレスを持っているわけである。なので、関数のアドレスを示すポインタ、というのも存在する。それがこの表現である。
もうちょこっと踏み込むと、今回の場合は、
int hogehoge(const void *, const void *)という関数のアドレスを渡す、という意味である。
このようにすることで、qsortからhogehogeを呼び出す(実行させる)ことができるのである。
こんな説明では何が何やらさっぱりですね、次回は実践編ということで、これを用いたソートプログラムの実装方法について解説したいと思います。
ここまで読んで
「ソートプログラムとか単純だし、自分で作れよ」
というのは、実はわりと正論で、ソートはちょっと理解すればすぐに作れます。
しかし、高速なソートとなると話は別。
C標準のqsortは実装はコンパイラにより多少異なりますが、おおむねアセンブラレベルでの最適化まで図られている超高速プログラムです。素人が組んだソートプログラムとは雲泥の差です。
ためしに10万個、8文字のパスワード(a-z、0-9からなる8文字の文字列)をソートさせてみました。
結果、20回平均わずか0.3秒で完了しました。恐ろしい速度です。
素人が適当に組むと軽く10倍は超えます。
あとは、冒頭にも書きましたがqsortは実装が本当に愉快で、C言語らしい関数なので、ぜひ紹介したいと思った次第です。
解説するにあたってひとつ面白い標準関数があるので、これを使って紹介したいと思う。
それはずばり、qsort。その名のとおり、ある配列に対してソートを行ってくれる関数である。
まずこの関数の定義
void qsort(
void *base,
size_t num,
size_t size,
int (*compare)(const void*, const void*)
)
引数は合計4つ、なかなか愉快な表現になっています。
順番に見ていくと
1、void *base
void *で定義された引数。void *って何やねん、と思うけれども、そもそも
ポインタの教え方がよろしくないと思う。
*hogehoge(変数名)というのは、すべからくアドレスのみを表している。それがどんなものであれ、hogehogeの中身は4バイトのアドレスを指し示すものである。
では、その前についてるintとかfloatとかの型が何をさしているのかというと、そのデータがどこまで入っているのか、ということである。
つまり *hogehogeがデータの開始位置、その前の型データがその開始位置からどこまでデータが入っているのか、という長さを表している。
この場合のvoidは不定を表している。
つまりqsortは長さが不明だけれど、アドレスの値を受け取っている。というわけ。
当然、そのままではデータは取り出せない、どこからどこまでデータなのかが分からないので、参照ができない。
そこで、出てくるのが2番目と3番目の引数。
2番目の引数は、配列の長さで、三番目の引数が変数の長さ。この変数の長さ、というのが型情報に相当するもので、2番目の値は配列の長さである。
そして極め付けが4番目の引数。C勉強してる人でも、ここはすっとばして教えることが多いので分からないかもしれない。
これは、関数ポインタと呼ばれるもの。
プログラムがメモリ上に配置される。
つまり、プログラムコード自身もアドレスを持っているわけである。なので、関数のアドレスを示すポインタ、というのも存在する。それがこの表現である。
もうちょこっと踏み込むと、今回の場合は、
int hogehoge(const void *, const void *)という関数のアドレスを渡す、という意味である。
このようにすることで、qsortからhogehogeを呼び出す(実行させる)ことができるのである。
こんな説明では何が何やらさっぱりですね、次回は実践編ということで、これを用いたソートプログラムの実装方法について解説したいと思います。
ここまで読んで
「ソートプログラムとか単純だし、自分で作れよ」
というのは、実はわりと正論で、ソートはちょっと理解すればすぐに作れます。
しかし、高速なソートとなると話は別。
C標準のqsortは実装はコンパイラにより多少異なりますが、おおむねアセンブラレベルでの最適化まで図られている超高速プログラムです。素人が組んだソートプログラムとは雲泥の差です。
ためしに10万個、8文字のパスワード(a-z、0-9からなる8文字の文字列)をソートさせてみました。
結果、20回平均わずか0.3秒で完了しました。恐ろしい速度です。
素人が適当に組むと軽く10倍は超えます。
あとは、冒頭にも書きましたがqsortは実装が本当に愉快で、C言語らしい関数なので、ぜひ紹介したいと思った次第です。
家帰って時間もあるけどやる気がしない
何かやりたいけどやる気がしない時期が一番つらい。
何やってても面白くない、時間の無駄と感じてしまう。
何をやってもイライラしてばかりでうまくいかない。
別に難しく考えずに楽しめばいいのにね。
自分から何かをやる、っていうのは本当にパワーが半端なく必要になると思う。
やり始めた時点で既に5割は目標に近づいてるんじゃないかなぁ、と思うわけで。
とりあえず今はだらだらと本でも読んですごす毎日です。
何かやりたいけどやる気がしない時期が一番つらい。
何やってても面白くない、時間の無駄と感じてしまう。
何をやってもイライラしてばかりでうまくいかない。
別に難しく考えずに楽しめばいいのにね。
自分から何かをやる、っていうのは本当にパワーが半端なく必要になると思う。
やり始めた時点で既に5割は目標に近づいてるんじゃないかなぁ、と思うわけで。
とりあえず今はだらだらと本でも読んですごす毎日です。
@fortran
プログラムの挙動がおかしい
デバッカ使う
ソースは正しいのになんかうまく動かん
スタック領域とかそこらへんのメモリ周りの設定いじる
やっぱだめ
アセンブラ出力する
なんかポップ命令おかしいの入ってね? これ取り出せないよね?
プログラムを切り出して再現性を確認する
再現性あるっぽい、原因としてしコンパイラを疑う
コンパイラのソースコード見る
バグっぽいの見つける
公式言ったらなんかバージョンアップで対応されてた
↑いまここ
原因はstレジスタ周りの処理が上手くいってなかったっぽいう。
ちなみにおそらくもっともメジャーであると思われる、GNUコンパイラでの話です。
プログラムのデバックしてるのかコンパイラのデバックしてるのかわからなくなる、それがfortran
大体数人がかりでレビューしてて明らかにコーディングに異常ないのがはっきりしてるんだし、最初からコンパイラを疑うべきだった。
C言語がそもそも言語としてもコンパイラとしても完成度が高すぎるんだよね(ここでいう完成度、というのは仕様にどれだけ忠実か、という点においてのみ。元の仕様がどんだけひどいものであっても、それを忠実に再現しているコンパイラがあれば、それは完成度の高い言語といえる)
その恵まれたその環境で育つと、あたかもコンパイラは神様のようなもので、コンパイラにミスはありえなくて、自分のプログラムにミスがあるに違いない、と思い込んでしまうのよね。
コンパイラも人間が作ったプログラムに過ぎないのにね。
ちなみにCコンパイラもちらっとのぞいてみたけど、ちょっと何いってるか分からなかった。
あのソース書いた人絶対頭おかしいわ
プログラムの挙動がおかしい
デバッカ使う
ソースは正しいのになんかうまく動かん
スタック領域とかそこらへんのメモリ周りの設定いじる
やっぱだめ
アセンブラ出力する
なんかポップ命令おかしいの入ってね? これ取り出せないよね?
プログラムを切り出して再現性を確認する
再現性あるっぽい、原因としてしコンパイラを疑う
コンパイラのソースコード見る
バグっぽいの見つける
公式言ったらなんかバージョンアップで対応されてた
↑いまここ
原因はstレジスタ周りの処理が上手くいってなかったっぽいう。
ちなみにおそらくもっともメジャーであると思われる、GNUコンパイラでの話です。
プログラムのデバックしてるのかコンパイラのデバックしてるのかわからなくなる、それがfortran
大体数人がかりでレビューしてて明らかにコーディングに異常ないのがはっきりしてるんだし、最初からコンパイラを疑うべきだった。
C言語がそもそも言語としてもコンパイラとしても完成度が高すぎるんだよね(ここでいう完成度、というのは仕様にどれだけ忠実か、という点においてのみ。元の仕様がどんだけひどいものであっても、それを忠実に再現しているコンパイラがあれば、それは完成度の高い言語といえる)
その恵まれたその環境で育つと、あたかもコンパイラは神様のようなもので、コンパイラにミスはありえなくて、自分のプログラムにミスがあるに違いない、と思い込んでしまうのよね。
コンパイラも人間が作ったプログラムに過ぎないのにね。
ちなみにCコンパイラもちらっとのぞいてみたけど、ちょっと何いってるか分からなかった。
あのソース書いた人絶対頭おかしいわ
実践、やばすぎるソース公開その2
今回のは本当にやばかった、気が狂うかと思ったまじで
CじゃないけどわかりやすいようにCっぽく意訳してみた
main(){
int x,y;
y=func1(&y,&x)
}
int func1(int *a, int *b){
int c
c=*a
*a=*b
*b=c
return c
}
書いた奴の頭の中をのぞいてみたい。
下のfuncはまだかろうじてわからなくもない、名前がfunc1ってのを除けば。
しかし、その用途が不明すぎる。
しかもこの関数が呼ばれていたのは、この一か所だけ。謎が深まるってレベルじゃねーぞおい。
これを解読するのに軽く3時間はかかった。わざわざこうやって設計してある理由を探すのに、である。
ちなみに、一行で書くとこうなる
x=y;
ちなみにfunc1にはF91もかくやという機能が搭載されていた。なんと、func2~5という名前の質量をもった分身が存在しているのだ! しかも、それらは全く同じ用途で用いられている。
さらに、xは一度も利用されておらず、yはglobal変数で宣言されているあたりも相当にキてる。
今回のは本当にやばかった、気が狂うかと思ったまじで
CじゃないけどわかりやすいようにCっぽく意訳してみた
main(){
int x,y;
y=func1(&y,&x)
}
int func1(int *a, int *b){
int c
c=*a
*a=*b
*b=c
return c
}
書いた奴の頭の中をのぞいてみたい。
下のfuncはまだかろうじてわからなくもない、名前がfunc1ってのを除けば。
しかし、その用途が不明すぎる。
しかもこの関数が呼ばれていたのは、この一か所だけ。謎が深まるってレベルじゃねーぞおい。
これを解読するのに軽く3時間はかかった。わざわざこうやって設計してある理由を探すのに、である。
ちなみに、一行で書くとこうなる
x=y;
ちなみにfunc1にはF91もかくやという機能が搭載されていた。なんと、func2~5という名前の質量をもった分身が存在しているのだ! しかも、それらは全く同じ用途で用いられている。
さらに、xは一度も利用されておらず、yはglobal変数で宣言されているあたりも相当にキてる。
ブラックかと思ったら神会社だった
2012年5月15日 お仕事 コメント (3)仕事が楽しくて仕方ない。特に社長が神すぎてやばい
人生で尊敬できる人ランクベスト3に入る
人格者ランクでいえば今のところトップ
社長のためならまじでただで働いてもいいと思えるレベル
今日の話なんだけれど、取引先で、サーバーのメンテナンス中に、OSアップデートかけたらシステムがつながらなくなる不具合発生、長時間サーバーがダウンしてしまうという旨が現場から電話で届いたのだけれど、社長は優しい声で一言。
「ああ、なんとかするから心配しなくていいよ」
で、取引先に電話一本。取引先の社長とは知り合いで、いいよいいよ、で済んでしまう。
リアルでこんなん初めて見た、超かっこええ
具体的な話は避けるけれど、取引先はリアルタイムでサーバー使っていたので、普通なら損害賠償すらあり得たレベル。社長の人格と人脈がなければ絶対に無理なことだと思う。
社長というとぎらぎらしてるイメージだけど、個人営業の喫茶店のような、すげぇまったりした感じ。奥さんからマドレーヌ貰ったりもしたし、会社というより家族みたいな。ほんとにこんなんで会社大丈夫なの? と思ってしまうほどである。
俺はまだ会社に通って1週間ほどだけど、じゃがりこ一緒に食いながら雑談する仲である。
楽しく仕事ができるのは幸せだと思う
人生で尊敬できる人ランクベスト3に入る
人格者ランクでいえば今のところトップ
社長のためならまじでただで働いてもいいと思えるレベル
今日の話なんだけれど、取引先で、サーバーのメンテナンス中に、OSアップデートかけたらシステムがつながらなくなる不具合発生、長時間サーバーがダウンしてしまうという旨が現場から電話で届いたのだけれど、社長は優しい声で一言。
「ああ、なんとかするから心配しなくていいよ」
で、取引先に電話一本。取引先の社長とは知り合いで、いいよいいよ、で済んでしまう。
リアルでこんなん初めて見た、超かっこええ
具体的な話は避けるけれど、取引先はリアルタイムでサーバー使っていたので、普通なら損害賠償すらあり得たレベル。社長の人格と人脈がなければ絶対に無理なことだと思う。
社長というとぎらぎらしてるイメージだけど、個人営業の喫茶店のような、すげぇまったりした感じ。奥さんからマドレーヌ貰ったりもしたし、会社というより家族みたいな。ほんとにこんなんで会社大丈夫なの? と思ってしまうほどである。
俺はまだ会社に通って1週間ほどだけど、じゃがりこ一緒に食いながら雑談する仲である。
楽しく仕事ができるのは幸せだと思う
実践、やばすぎるソース公開その1
~オブジェクト指向言語編~
あるクラスAにpublicで「関数」という関数が存在してました
別のクラスBにpublicで「関数」という関数が存在していました。
別のクラスCにpublicで「関数」いう関数が(ry
別のクラスDにpublicで「関数」(ry
別のクラスE(ry
別(ry
………
プロジェクト内に合計10か所発見されました。
ちなみに中身は定数が変わるだけで全く一緒ですおいふざけんな窓からPC投げ捨てんぞ
名前が日本語表記だとか、なぜ同じ名前にしたのかとか、突っ込みたいところは山ほどある。おまけになぜpublicにしたのか全く分からないし、極めつけは中身が全部同じだってことです。ほぼ同じものをコピペするなら標準化しとけよと激しく突っ込みを入れたい。
~オブジェクト指向言語編~
あるクラスAにpublicで「関数」という関数が存在してました
別のクラスBにpublicで「関数」という関数が存在していました。
別のクラスCにpublicで「関数」いう関数が(ry
別のクラスDにpublicで「関数」(ry
別のクラスE(ry
別(ry
………
プロジェクト内に合計10か所発見されました。
ちなみに中身は定数が変わるだけで全く一緒ですおいふざけんな窓からPC投げ捨てんぞ
名前が日本語表記だとか、なぜ同じ名前にしたのかとか、突っ込みたいところは山ほどある。おまけになぜpublicにしたのか全く分からないし、極めつけは中身が全部同じだってことです。ほぼ同じものをコピペするなら標準化しとけよと激しく突っ込みを入れたい。
最初の仕事は社長と常務にm言語教えることだった・・・
色々あって入社前にプロジェクトリーダーになりそうです
「なれる!SE」でももっと遅かっただろwwwwwwww
色々あって入社前にプロジェクトリーダーになりそうです
「なれる!SE」でももっと遅かっただろwwwwwwww