C のかけら

小さい C プログラムを集めています。自分用のメモ書きです。同じ動作をする Ruby のプログラムと比較しても、意外に複雑な処理を簡単に記述できるのに驚かされます。先輩達がなんでもかんでも C 言語で作って行ったのが分かる気がします。しかし、ポインタの操作やメモリ管理など初心者には地雷があちこちに埋まっていそうです。Ruby でできることは Ruby でやった方が安心です。

C の関数の使い方が分からなくなったら man sprintf のように man ページを参照できます。ヘッダーファイルは /usr/include/stdio.h のように /usr/include/ 以下に納められているようです。locate で探すのも便利です。Unix は C 言語の開発環境なのだというのが実感として分かります。

C のソースをスクリプト感覚で試すには

runソースをコンパイルして実行するシェルスクリプト。run foo.c arg1 ... のように使います。

入出力/引数の渡し方

cat.ccat.rb標準入出力の使い方
args.cargs.rbコマンドライン引数の受け取り方
options.coptions.rbオプションの受け取り方
getopt.cGNU getopt を利用したオプションの受け取り方。高度な getopt の使い方は man 3 getopt のサンプルプログラムを参照してください。
varargs.cvarargs.rbstdarg.h を利用して不定長の引数を持つ関数を記述する。man stdarg 参照。
fopen.cfopen.rbファイルのオープンの仕方
file_open.cfile_open.rbファイルを開くときにファイルの有無をチェックする関数。stddef.hでの NULL の定義は ((void *)0) で void 型へのポインタです。
tty.ctty.rbttyから直接文字を拾う
ttyin.cttyin.rb標準エラーとttyスキャンで標準入出力に影響しない対話型プログラムを作る。run ttyin.c < cat.c > temp.txt で試せます。
buf.cbuf.rbバッファーを介してファイル/ストリームに入出力する。
trim.ctrim.rb行末の改行を取り除く。ls | run trim.c で試します。
popen.cpopen.rbパイプをオープンします。
system.csystem.rb他のプログラムを実行する方法。C も立派なグルー言語です。
scanf.cscanf.rb文字列をコンソールから読みこむ
number.cnumber.rb数値をコンソールから読みこむ
rand.crand.rb乱数

文字列処理

pointer.cpointer.rbポインタの使い方。出典は K & R です。
array.carray.rb不定要素数の配列の処理。ポインタのポインタの使い方。
func_call.cfunc_call.rb関数へのポインターの配列
malloc.cmalloc2.c動的メモリ配置
strcmp.cstrcmp.rb文字列の比較
strcat.cstrcat.rb文字列の結合
sprintf.csprintf.rb文字列の結合その2
strcpy.cstrcpy.rb文字列のコピー
strdup.cstrdup.rbstrdup 文字列のコピーその2
strsearch.cstrsearch.rbstring.h の中の文字列検索関係の関数のテスト。各々の関数の使い方は man strstr などで確認してください。
strtok.cstrtok.rbstring.h の strtok 関数を使うと文字列からトークンを切り出すことができます。二回目以降は第一引数に NULL を指定すると順次トークンが取りだせます。
comment.ccomment.rbコメントを消去するプログラムです。string.h の応用。入れ子構造には対応していません。
pointer.html ポインタの読み方

ユーティリティー

hex.chex.rb整数を16進数表記に変換する
chkhex.cchkhex.rb16進数を10進数に変換する
printbits.cprintbits.rbビット操作の例。10進->2進変換。C言語入門(Les Hancock, Morris Krieger, Saba Zamir著)からの引用
cursor.cVT100.rbVT100 のエスケープシケンスを利用してカーソルの制御をします。参照:エスケープシーケンス
menu.cmenu.rbメニュー表示プログラム。ちょっとだけ実用的? Cプログラミング診断室(藤原博文著)を真似て作りました。構造体の勉強用です。
qsort_test.csort.rbstdlib.h の qsort 関数を利用したソートプログラム。
strsort.cstrsort.rbqsort 関数を利用した文字列のソート。整数のソートの comp 関数では *(int *)p1 ですが文字列の場合 *(char **)p1 となっているのがミソです。
structsrt.cstructsrt.rbqsort を利用して構造体をソートします。
b_search.cnumindex.rbstdlib.h の bsearch 関数を利用して整数の配列の二分木検索を行います。検索を開始する前にデータを qsort で昇順にソートしておきます。bsearch 関数は key の要素が見つかればその要素へのポインターを返し、見つからなければ NULL を返します。
strsrch.cstrsrch.rbbsearch 関数を使った文字列の検索
structsrch.cstructsrch.rb構造体の検索
list.clist.rbリスト構造
hash.chash.rbハッシュ探索。プログラミング言語 C 第2版からの引用。
perror.c システムコールで発生したエラーを報告する関数 perror
server.cclient.c超原始的なクライアント・サーバープログラム。gcc -o server server.c と gcc -o client client.c でサーバーとクライアントのプログラムをコンパイルします。それから ./server localhost & でサーバーをバックグラウンドで走らせます。次に ./client localhost を実行すると hello, world と表示されます。server を止めるには、fg でフォアグラウンドで走らせて、Control-C でプログラムを中止します。
hello.cinetd の使い方inetd を利用すると簡単にサーバーを作れます。inetd がソケットの管理をしてくれるのでプログラム側は普通に stdin, stdout, stderr に入出力するプログラムを記述するだけです。詳しくは右の inetd の使い方 を見てください。
helloworld.chelloworld.rbGTK+ 1.2 tutorial より、GTK+ プログラミングのサンプル。コンパイルは gcc -Wall -g helloworld.c -o helloworld `gtk-config --cflags` `gtk-config --libs` で行います。同じサンプルプログラムを Ruby/GTK で書いたものが helloworld.rb です。ruby helloworld.rb で実行します。GTK+ 2.0 TutorialRuby/Gtk Tutorial

プリプロセッサ/マクロ

sizeof.c 引数つきマクロのテスト。マクロがどのように展開されたかは、gcc -E sizeof.c で分かります。
header.hexmain.c
func1.c
分割コンパイルのヘッダーファイルの例。EXTERNAL マクロを定義することでヘッダーファイルの関数の冒頭に extern をつけたり取ったりできます。gcc -E exmain.h で確認できます。実行ファイルをコンパイルするにはfunc1.c を含めて、gcc exmain.c func1.c とします。また、gcc -c exmain.c、gcc -c func1.c で分割コンパイルとしてオブジェクトファイル exmain.o と func1.o を作った後、gcc exmain.o func1.o としても実行ファイル a.out を作成できます。
debugprint.hdebug.cデバック情報を表示する関数のマクロです。debug.c をコンパイルして試した後、ソースの DEBUG をコメントアウトしてもう一度コンパイルするとデバッグ情報は出なくなります。Cプログラミング診断室(藤原博文著)からの引用です。

標準ライブラリ

hsum.rb 条件コンパイルでゴチャゴチャしているヘッダからマクロと関数宣言だけを抜きだす Ruby スクリプト。ruby hsum /usr/include/error.h のように使います。
assert.c assert( expression ) 関数は、expression が偽であればエラーメッセージを表示してプログラムを中止します。assert.h を include して使いますが、マクロ NDEBUG が定義されていると assert は実行されません。
ctype.c ctype.h の使い方。ここに宣言されている関数は文字クラスのテストで偽のときに 0 真のときにはその他の整数を返します。tolower と toupper は英字の大文字・小文字の変換をします。
errno.c errno.h に定義されたエラーコード。error.h にはエラー表示関数が宣言されています。
math.c math.h ライブラリーを利用した場合は、gcc -lm math.c のように数学ライブラリーを指定しないとコンパイルできません。run math.c ではエラーになってしまいます。-lm の m がライブラリー名ですが、/usr/lib/libm.a の lib と .a を取った m がライブラリー名だそうです。コンパイルした実行ファイルがどのようなライブラリーを使っているかは、ldd a.out のようにして確認できます。
stdio.c stdio.h にはファイル/ストリーム関係の 関数とマクロが宣言されています。
stdlib.c stdlib.h には文字数字の変換、乱数、メモリ操作、システム関連、検索、整数の関数、マルチバイト文字に関係した関数などが宣言されています。
string.c string.h には文字列処理の関数が宣言されています。
time.c time.h には時刻処理の関数が宣言されています。

イディオム

counter.ccounter.rbstatic 変数の使い方。この他に static 宣言はソースファイルの外部から変数や関数を参照できないようにするためにも利用されます。
swap.cswap.rb変数の値の交換
str_cpy.cstr_cpy.rb文字列のコピー
str_cat.cstr_cat.rb文字列の連結
str_len.cstr_len.rb文字列長の計算
str_cmp.cstr_cmp.rb文字列の比較
head.chead.rbリスト構造の探索の慣用句
loop.c ループの慣用句、記法を統一すれば誤りを減らせる。プログラミング作法 Brian W. Kerningam and Rob Pike からの引用
forkchild.c 子プロセスを発生させる方法の慣用句
forkexec.c 他のプログラムを子プロセスとして走らせる方法の慣用句

システムコール

syscall.txtsyscall.rbシステムコールのリスト。/usr/include/asm/unistd.h から抽出。__NR_ を取り除いたものがシステムコールの名前になります。システムコールが呼ばれると、その名前に割り当てられた番号を使ってソフトウェア割り込みをかけるのですが、命令が実行されるタイミングを別にすれば、システムコールも普通の関数のように使うことができます。各関数の使い方は man 2 time のようにマニュアルページのセクション2を指定して参照します。
systime.csystime.rbtime()。Ruby にはシステムコールを直接行う syscall メソッドもあります。しかし、Ruby では大抵のシステムコールと同じことを行うメソッドが用意されているので、それを使った方が互換性や安全性からも有利です。
syscat.csyscat.rbread() と write()
sleep.csleep.rbsleep()
sysopen.csysopen.rbファイルの open() と close()。run sysopen.c filename で試します。
execl.cexecl.rbexec ファミリーは現在のプロセスを終了して引数で与えられるプログラムを実行します。プロセスIDはそのまま引き継がれます。execl() の第1引数はプログラムのフルパス名、第2引数はプログラム名、第3引数以下はコマンドライン引数です。引数のリストは最後に NULL で終了する必要があります。exec ファミリーには execl, execlp, execle, execv, execvp, execve がありますが、execve だけがシステムコールで、後は execve のフロントエンドです。

セキュア・プログラミング

overflow.c バッファ・オーバーフローの起こし方。危険です。

C 全般

Damayan Page
パソコン初心者の館
小俣光之:コンピュータのページ(C言語講座)
Introduction to Programming in C, by Mark McKenzie
Code from The Practice of Programming Brina W.Kerningham and Rob Pike による同書のサンプルプログラム

安全なプログラム

IPA/ISEC セキュア・プログラミング講座
危険な関数群:strcpy(3), strcat(3), sprintf(3), vsprintf(3), gets(3)
代わりに使える関数群:strncpy, strncat, snprintf, fgets
ポインターはバグの巣窟である。ポインター不要論 - やまざき@BinaryTechnology

参考図書

プログラミング C 第2版, B.W.カーニハン/D.M.リッチー, 石田晴久訳, 共立出版
UNIXプログラミング環境, B.W.Kerninghan, Rob Pike, 石田晴久監訳, ASCII
プログラミング作法, B.W.Kerninghan, Rob Pike, 福崎俊博訳, ASCII
C言語入門 Les Hancock, Morris Krieger, Saba Zamier, 倉骨彰/三浦明美訳, ASCII
標準 C ライブラリ, P.J.ブラウガー, 福富寛/門倉明彦/清水恵介訳, トッパン
C 言語によるアルゴリズム事典, 奥村晴彦, 技術評論社
C プログラミング診断室, 藤原博文, 技術評論社
C プログラミング専門課程, 藤原博文, 技術評論社
UNIX システムコールハンドブック, 大倉仰一/谷田部賢一
UNIX 標準ライブラリハンドブック, 日下部建/谷田部賢一

あとがき

趣味で C の入門書を読んでも 1 + 1 = 2 のようなプログラムしか書けないので余り面白味が感じられませんが、それは、知っている関数の数が圧倒的に少ないせいです。一応プログラムらしき物を作る為の C の知識はそれ程多くはありませんが、実用になるプログラムを書くためには C の文法的な知識だけでは不可能で、目的にあった多くの関数の所在と性質の知識が不可欠です。C ではほとんどの機能を関数として取り扱うことによって、言語自体の仕様が巨大化することなく様々な現実の要求に対処するプログラムを書くことができます。したがって、言語を習得したと思えるまでの時間が短いにもかかわらず多種多様なプログラムが書けるのです。そうは言っても、複雑な問題を解決するためのプログラムの作成をするためには、それに見合った知識の量が要求されます。

関数による詳細なアルゴリズムの隠蔽はプログラムに限らず、複雑なシステムを扱う場合の基本的な考え方のような気がします。汎用性があって効率のよい関数やアルゴリズムの知識を多く持った人のプログラムの生産性がそうでない人の100倍あると言われても頷けます。

したがって、素人が楽しみでプログラムをするのためには C よりもスクリプト言語の方が便利です。抽象的な機能(たとえばメモリー管理や、ソート、検索など汎用性のある機能)について細部を気にせずプログラムできるからです。Ruby なら、タイポやメモリー管理のバグに悩まされずに自分が作りたいプログラムを素早く作ることができます。行末の ; が要らないだけでもデバッグに要する時間が激減します。また、システムコールを使わないとできないようなハードよりのプログラムも特に工夫もせずに記述することができます。また、スクリプトの動作速度についても、ハードの性能が向上しているので気にならない場合が多いと思います。とはいえ、Ruby は C で記述されているので C の性質も色濃く持っています。ある程度 C を勉強することには Ruby の動作をより良く理解できるというメリットがあります。