Dokusyo-nissi Bessitu 2007-07-04 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第9章記憶クラス storage class 変数の行動 behavior (範囲) を変えること。 9.1. 記憶クラスとはなにか? 関数の中の変数はその関数が現われるごとに (変数の) 値を失うことに気づくだろ うがこれは効率の上からそうなっている。 OS にはそのデータが再度必要になるか どうかはわからないのでプログラムがシステムに戻されると割り当てたメモリを解 放する。 9.2. auto 初期設定 default では C の変数に auto 記憶クラスが使われる。これは必要時に 自動的に変数がつくられるためそう呼ばれ領域 scope の外側では失効 fall する。 auto 記憶クラスはキーワード auto を変数の宣言の前につけることで変数に指定で きるがこれは効果はない。このキーワードは他の記憶識別子 storage identifier とのつりあいのために language に導入されたものだ。 9.3. static 静的変数 static variable はそれが領域を抜け出ても削除されることのない変数で あり関数が呼び出される間は常にその値を保持する。これが例題だ。 Example 9-1. list_squares.c #include int get_next_square(void); int main() { int i; for (i = 0; i < 10; i++) printf("%6d\n", get_next_square()); printf("and%6d\n", get_next_square()); return 0; } int get_next_square() { static int count = -1; count += 1; return count * count; } これは 0 から 10 までの数字を 2乗して表示 list する。 0 から 9 まではループ により表示されその後ですぐ 10 の 2乗の値が表示される。 2007-07-08 φ(-_-) ■[lang]はじめての C [Learning GNU C] 9.4. extern extern はその変数がすでに外部の関数またはファイルに存在することを意味するの で extern をつけて変数を宣言するとプログラムは実際にはそれへのメモリを用意 reserve しない。 開発中にある変数を全てのファイルでつくりたい場合 1つのファイルで全体として globally 宣言する。それは関数には含まずその変数の extern 宣言をヘッダファイ ルに追加する。 9.5. register register という記憶クラスは頻繁に使用しアクセスする変数をコンパイラによって 効率化させる手がかり hint として使われる。変数は通常メインメモリ normal memory (RAM) に記憶され必要になるとコンピュータの処理装置 processor のあち らこちらに渡される。データの送信速度は相当高速だが改良を加えることができる 。ほとんど全てのコンピュータ処理装置は cpu 記憶回路 cpu register からなり実 際は処理装置上のメモリスロットだ。ここに記憶されたデータはメインメモリから データを取り出すという付加 overhead (処置) を免れる。このメモリはメインメモ リと比べて非常に小さいのだがほんのわずかな変数のみを記憶することができる。 GCC は常にアクセス数の多い変数がなにかを考え決めて記憶回路 register を使用 する。それはうまく働くがけっして完全ではない。なぜなら GCC にはそのプログラ ムの目的はわからないからだ。 register というキーワードを用いることで効率化 にはなにが必要かを GCC に知らせることができる。 cpu 記憶回路で変数を保持するのにともなう 1つの問題はポインタがもてないこと だ。ポインタはメインメモリのみを指し示すことができる。こうした制限のため GCC ではプログラムの中のポインタをとるアドレスをもった変数ではキーワードの register は無視される。 プログラムをつくりそこで cpu 記憶回路に変数を保持させようとすると結果は OS がこうした要求を無視するか引き受けるか honour 次第となる。 2007-07-10 φ(-_-) ■[lang]はじめての C [Learning GNU C] 9.6. 制約型識別子 restrict type qualifier 思うに、これはポインタを使ったなんらかの実装であって、特定したポインタがメ モリのある区域のみのポインタであることをコンパイラへ知らせる。これを知って いることでより効率的なコードができると考えられる。 9.7. typedef typedef は他 (のキーワード) とはまったく異なり変数の型に新しい名前をつける のに使われる。それを使うのには主に 2つ理由がある。もっとも一般的なのは定義 した構造体に名前をつけることで、その結果通常のように struct というキーワー ドを前につけずに新しいデータ型を使うことができる。 typedef を使う 2つ目 (の理由) は互換性 compatibility だ。そう、32 bit の数 字を記憶させたいとする。仮りに int (型) を使ったとしてそれがどの機械でも 32 bit だとは保証されない。これを切りぬけるため新しい型で正確なサイズをとる typedef を使用するようプリプロセッサに指令することができる。 Example 9-2. battleship.c #include /* 形式、座標上の位置、装備 (ミサイル) */ struct _ship { int type; int x; int y; int missilies; }; typedef struct _ship ship; int main() { ship battle_ship_1; ship battle_ship_2 = {1, 60, 66, 8}; battle_ship_1.type = 63; battle_ship_1.x = 54; battle_ship_1.y = 98; battle_ship_1.missilies = 12; /* 実際に 使う コードが ここに 書かれる */ return 0; } 2007-07-14 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第10章 C プリプロセッサ いつどのように使うか 10.1. プリプロセッサとはなにか C プリプロセッサはコンパイラに渡す前にソースコード上で走らせる簡単なマクロ の拡張のことだ。ハッシュ記号 '#' から始まる行でプリプロセッサに指令する。 C の式である名前を割り当てるマクロをつくるとちょうどその式で使われたように その名前をコードで使うことができる。プリプロセッサは (当てはまった) 名前の 全てをその式と取り換える。 10.2. なんのために使うか? マクロはコンパイルする前の過程で与えられるコードの断片だ。"#define statements (命令文)" というマクロが C プリプロセッサにより実行される。 1つ コードを見てみよう、 Example 10-1. box_of_stars.c #define SIZE_OF_SQUARE 4 int main() { int i, j; for (i = 0; i < SIZE_OF_SQUARE; i++) { for (j = 0; j < SIZE_OF_SQUARE; j++) printf("*"); // 各列 each row に アスタリスクを 表示 printf("\n"); // 各列の 終わりで 改行 } return 0; } このコードの出力は長方形 box になる。 **** **** **** **** C プリプロセッサは単にマクロの SIZE_OF_SQUARE を 4 の値と取り換える。これは 2つの理由からとても役立つ。 1つは、行を編集するだけで長方形のサイズを変更することができる。例題だと SIZE_OF_SQUARE を2度使うだけなので大きな利点はないがより大きなプログラムで はこれで管理 life はより簡単になり値の変換を忘れる可能性がとり除かれる。 2つ目は、コードをより読みやすくし例えば値に "#define PI 3.14285713" のよう に意味のある名前をつけることができる。 10.3 いくつかのマクロサンプル glibc のいくつかの小さな関数はマクロで実装され getc() はその 1つだ。 2007-07-15 φ(-_-) ■[lang]はじめての C [Learning GNU C] 10.4. マクロへの警告 caveat マクロはまちがった使い方ができる。コードがコンパイルを始めるともはやマクロ は存在しないのでバグをつかまえるのは困難だ。小さな例題を取り上げよう。 Example 10-2. max_macro.c #define MAX(a, b) (a > b ? a : b) int main() { int cows = 10, sheep = 12; printf("We have %d of our most common animal.\n", MAX(cows, sheep)); return 0; } コンパイルし実行すると (下の出力が) 得られる。 ciaran@poor:~/book$ ./max_macro We have 12 of our most common animal. ciaran@poor:~/book$ そう、全て良好に見える。次の例題を試してみよう。 Example 10-3. max_macro_problem.c #define MAX(a, b) (a > b ? a : b) int main() { int cows = 10, sheep = 12; printf("We have %d of our most common animal.\n", MAX(cows, sheep)); printf("Hang on, we just bought another one.\n"); printf("Now we have %d.\n", MAX(cows, ++sheep)); return 0; } なにが起きるかがわかるだろうか? ciaran@poor:~/book$ ./max_macro_problem We have 12 of our most common animal. Hang on, we just bought another one. Now we have 14. ciaran@poor:~/book$ text の置き換えが生じると "++sheep" が 2度要求 instance される。別のもっと 意地の悪い方法ではマクロへの引数として関数が使われ (コードに) 渡されてその 正体を現わすだろう。この関数で global または static 変数を変更すれば変更が 2度 multiple times 生じるだろう。これらのバグは見つけるのがとても困難だ。コ ードは完全に有効なのでコンパイラは全く警告をしない。このバグは実行時にのみ 見つかりマクロが呼び出されそれが副作用をもつ引数を伴う場合にだけ起きる習性 wont をもつ。 2007-07-20 φ(-_-) ■[lang]はじめての C [Learning GNU C] 10.5. マクロは必要か? マクロは language の小さな欠点を補うためによく使われた。しかし language の 進化によりこうした試み defiance はほとんど脇へと追いやられた。プリプロセッ サのマクロを理解しその使い方を知ることは今でも役にたつしそれはとても一般的 だ。より長いものと置き換えるための部品 part としてマクロは人々に使われるよ うになった。 10.6. 簡単なマクロの代用 簡単な "#define" を const int 型のグローバル変数 (大域変数) global variable に取り換えることを考えたとすればそれは正しい。 const 変数にはいくつか長所が あり 1つの小さな強みが値をポインタを通して渡す必要のあるときそのアドレスを 取得することができることだ。このやり方はマクロと比べてより柔軟性がある。仮 りに const 変数のアドレスを獲得できない場合には GCC が (変数を) "#define" とできるだけ同じ水準となるよう活用 optimise させるようにできる。 10.7. 複雑なマクロの代用 複雑なマクロ、これはマクロとして実行される関数のこと。インライン関数 inline function により置き換えが可能となる。 第11章可変引数 variable length argument VA_ARGS マクロ、その他のこと 11.1 可変引数とはなにか? 可変引数にはあるものとしてすでに出会っている。 printf() を思い出そう。どれ だけの (長さか) わからない引数ではこの関数の原型 prototype をどう書いていた だろうか? 2007-07-22 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第12章関数の秘訣 trick 12.1. 仮想関数 virtual function とはなにか? void データ型の別の使い方に関数ポインタをつくることがある。これは十分進化し た fairly advanced プログラミングテクニックだが非常に役にたち満足するものに なる。これが関数ポインタの例題だ、 Example 12-1. virtual_function.c int main() { /* シマッタ、もっと 良いのを 書かないと ... */ return 0; } 12.2 内部関数 nesting function GCC では別の関数内にある関数の定義を認めている。こうして定義された関数は内 部関数といわれ変数と同じスコープの基準 scoping rule に従う。親の関数が抜け 出ると子の関数はスコープから消えて利用できなくなる。 12.3. 内部関数の利点 GCC で与えられる内部関数の柔軟性の理由はおそらく、その性能の向上が小さいに もかかわらず、正確に用いることで効果が得られるためだ。内部関数は字義どおり (内部の) スコープに従いそれを含んだ関数の変数として使うことができる。 こうした理由によりそれは引数としてポインタをとる関数が要求されるたいていの 仕事を成し遂げることができる。ポインタを使うとポインタをもつ変数は記憶回路 register で記憶できないという理由からほんのわずか実行時のロス performance loss ができる。ポインタはけっして機械の記憶回路を指し示さない、どうしてこの ポインタが使えるだろう? 12.4. 内部関数の宣言と定義 関数を定義すると ... Example 12-2. simple_nested_function.c #include int swap(int *, int *); int main() { int first = 12, second = 34; printf("f is %d and s is %d\n", first, second); swap (&first, &second); printf("f is %d and s is %d\n", first, second); return 0; } int swap(int *a, int *b) { int tmp; tmp = *a; *a = *b; *b = tmp; return 0; } 普通の関数のように内部関数を宣言する必要はないがもし望むなら (それは) 可能 だ。そうすることの唯一の理由は読みやすさのためだろうから関数をそれが使われ る位置の近くにもってくることは好ましいかもしれない。これは自分次第だが、内 部関数の宣言を決めたのなら自分でそれを確実に correctly 宣言しないといけない 。 12.5. スコープ scope 内部関数はローカルスコープ local scope をもっていて内部関数を extern で宣言 するとエラーになる。 static と inline とはともに有効だがこの static の意味 が (なにか) 思いつかない。 2007-07-27 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第13章コマンドラインに引数をとる 13.1. C ではコマンドラインの引数をどう扱うか? プログラムは OS が main() 関数プログラムを呼び出すことから始める。今までは それぞれのプログラムで main() を引数をとらない関数として定義したがどんな場 合もそうとは限らない。 main() は C の中で唯一 2つのやり方で定義することがで きる。それは引数をもたないか 2個または 3個の引数をとることができる。引数 3 個の形式は特に役にたたないし決して必要とはされない。これはこの章の終わりで 短く補おう。 引数 2個の形式では 1つの int 型と 1つの文字配列をとる。 main() で定義すると きこれらの引数にどんな名をつけてもいいが習わしとして "argc" "argv[]" と書く 。 1つ目の引数は 2つ目の引数で渡された文字配列の要素がどれだけそこにあるか を数えて保持する。配列は常に null で終わるため argv[argc] == NULL となる。 これが使い方を示す短いプログラムだ、 Example 13-1. list_args.c #include int main(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) printf("arg[%d] == %s\n", i, argv[i]); return 0; } main() には 2つの引数つまり "int" と "*char[]" (文字配列) が渡される。 int は普通 "argc" と呼ばれその名前からわかるように "argument count" の略だ。そ れは main() に渡される引数の数を保持する。 2つ目の引数は普通 "argv" と呼ば れ文字列の配列だ。 13-2. argp コマンドラインで引数をとる C の方式はとても簡単だがプログラムが多くのオプシ ョンをもつときはそれを複雑にすることができる。これを解決するため glibc では コマンドのタスクを実行する一連の関数を用意している。 "argp_*" 関数は多くの 仕事を実行しユーザにより親しみやすくする標準的なやり方でそれを行なう。これ が argp を使った短いプログラムだ、 /* ここへ 小さな argp の プログラムを 置く */ このプログラムを実行し確かめる ... 2007-07-29 φ(-_-) ■[lang]はじめての C [Learning GNU C] 13-3. argp の機能をさらに使う これは長めのプログラムで 4つのグローバル変数を使い自分のプログラムに関する 情報を保持する。 Example 13-3. better_argp.c #include #include const char *argp_program_version = "simple_argp 0.1"; const char *argp_program_bug_address = ""; static char doc[] = "short program to show the use of argp\nThis program does little"; static char args_doc[] = "ARG1 ARG2"; /* initialise an argp_option struct with the options we expect */ static struct argp_option options[] = { {"verbose", 'v', 0, 0, "Produce verbose output"}, {"output", '0', "FILE", 0, "Output to FILE"}, { 0 } }; /* Used by 'main' to communicate with 'parse_opt'. */ struct arguments { char *args[2]; /* ARG1 & ARG2 */ int silent, verbose; char *output_file; }; /* Parse a single option */ static error_t parse_opt(int key, char *arg, struct argp_state *state) { /* Get the INPUT argument from 'argp_parse', which we know is a pointer to our arguments structure. */ struct arguments *arguments = state->input; switch (key) { case 'q': case 's': arguments->silent = 1; break; case 'v': arguments->verbose = 1; break; case 'o': arguments->output_file = arg; break; case ARGP_KEY_ARG: if (state->arg_num >= 2) /* Too many arguments.*/ argp_usage (state); arguments->args[state->arg_num] = arg; break; case ARGP_KEY_END: if (state->arg_num < 2) /* Not enough arguments. */ argp_usage (state); break; default: return ARGP_ERR_UNKNOWN; } return 0; } /* Our argp parser */ static struct argp argp = {options, parse_opt, args_doc, doc}; int main(int argc, char **argv) { struct arguments arguments; /* Default values */ arguments.silent = 0; arguments.verbose = 0; arguments.output_file = "-"; /* Parse out arguments: every option seen by 'parse_opt' will be reflected in 'arguments'. */ argp_parse (&argp, argc, argv, 0, 0, &arguments); printf("ARG1 = %s\nARG2 = %s\nOUTPUT_FILE = %s\n" "VERBOSE = %s\nSILENT = %s\n", arguments.args[0], arguments.args[1], arguments.output_file, arguments.verbose ? "yes" : "no", arguments.silent ? "yes" : "no"); exit(0); } これってとても簡単だね? 13-4. 環境変数 environment variable 省略。その 4つの引数形式の使い方と環境変数を理解する別の方法とを説明、小さ な例題を示す。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ argp は標準ライブラリ getopt の機能を拡張しエラー処理も備えたスグレモノ。 $ less /usr/include/argp.h 例題の better_argp.c はもちろんコンパイルして実行できます。 $ cc -o better_argp better_argp.c $ ./better_argp --help Usage: better_argp [OPTION ...] ARG1 ARG2 short program to show the use of argp This program does little -O. --output=FILE Output to FILE -v, --verbose Produce verbose output -?, --help Give this help list --usage Give a short usage message -V, --version Print program version Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. Report bugs to . $ ./better_argp lets showtime ARG1 = lets ARG2 = showtime OUTPUT_FILE = - VERBOSE = no SILENT = no $ も一つ使い方がわかってない ...oLr 2007-08-02 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第14章ライブラリの使い方と書き方 コンパイルコードの再利用 14.1. ライブラリとはなにか? ライブラリは最初に実行される main() に含まれないことを除けばプログラムと似 ている。ライブラリにある関数はアプリケーションとライブラリとをリンクするこ とで別のアプリケーションに使うことができる。 14.2. ライブラリを使う ライブラリにリンクするにはライブラリ名に伴う switch "-l}" を使う必要がある 。 glibc の数学用ライブラリを "libm.so" と呼ぶがこのライブラリを使うための "lib}" または ".so}" は必要なくコンパイルラインに switch "-lm}" をつけ加え る。 14-3. コンパイルの段階 stage プログラムのコンパイルには多くの tool が必要となり gcc では行程 process の 各段階を制御 handle する別のプログラムを呼び出すことで処理する。 3つの主な 段階が前処理 preprocessing、編集 compilation およびリンク linking だ。 C の コードではハッシュ記号 "#" で始まる行はプリプロセッサへの命令 command であ り gcc には cpp (c preprocessor) というプリプロセッサが含まれている。 "# define" と "#include" が断然 (使われる) もっとも普通のプリプロセッサコマン ドだ。編集行程は多くのより小さな段階に細分 break down される。この編集の 1 つもまぎらわしいが編集と呼ばれる。編集とはソースコードを実行コード object code に変換する行程だ。プログラムが長くなるときには GCC に "-c" をつけて呼 び出すとファイルが再コンパイルできる最小のより小さなファイルにプログラムを 分割するよう変換してコンパイルする時間 time をとることができる。最初は単に ソースファイルをコンパイルするよう GCC に知らせないといけない。これは ... 14-4. ライブラリを書く ライブラリを書くのはプログラムを書くことに似ている。一番の明らかな違いは main() にないことだ。日常使う関数や別に使えることが判明したと考えられる関数 にはライブラリはとても便利だ。 14-5. 動的 dynamic または静的 static ライブラリは動的あるいは静的な方法でリンクすることができる。ライブラリの静 的なリンクではアプリケーション自体をコンパイルする。動的なリンクはより新し い方式でアプリケーションが実行時にライブラリとリンクすることが許されていて これは多くの利点をもつ。始めにライブラリはそのアプリケーションを再コンパイ ルする必要がなく書き換えることができその逆も可能 vice versa だ。 第15章役にたつ good コードを書く 重要な課題 project への検討 15.1. 読みやすさ readability これは役にたつコードの質でもっとも重要な 1つだ。幸いそれは経験 practice に ともない自然となる技術だ。読みやすさからの最大の悪口 detractor の 1つが「気 のきいた」 clever コードだ。多くの操作をかみ砕き scrunch 1つの行または命令 文に入れることはうまくやった achievement ように思えるが別の誰かがそれを読ん でみようとしたときそうした操作を頭の中でときほぐす unravel 必要が生じて (結 局は) 遅くなるだろう。 2007-08-05 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第16章速度 やさしい効率化 -- 低い位置で果実を摘む C は利用できる最速の高レベル language としてよく知られている。 16.1. 効率化について コードを効率的にするときは 2つある -- それを書いている間と実行してガッカリ した後とだ。 一般に 90パーセントのアプリケーションの実行時間ではコードによるものが 10パ ーセントを占めるといわれる。めったに呼ばれない関数の効率化から得るものは少 ない。 16.2. function attribute とはなにか? function attribute は C language での GNU の拡張の 1つだ。それはその関数に 関するより多くの情報を GCC へ伝えることを認めている。こうした情報は効率化や 厳密なチェックを含んだ多くの目的のため使われる。 16.3. function attribute のシンタックス function attribute は関数の宣言の一部で指定する。関数で引数をカッコで閉じた 後キーワード "__attribute__" と二重マルカッコの中の要求した attribute が続 く。これが "pure" attribute をもつ関数だ、 int my_func(int first, int second) __attribute__ ((pure)); 関数は複数の attribute をもつことができこれをするには二重カッコの中で (それ ぞれの) attribute をコンマで区切る。 16-4. pure および const とはなにか? pure 関数はそれ自身のスコープの外側にあるどんなものにも影響を与えない。これ はグローバル変数やそのポインタで渡された変数を読めてもそうした変数を書けな いことを意味する。それは揮発性 volatile の変数や外部のリソース (ファイルの ような) から読まないはずだ。 const は pure のより厳密なかたちであり関数がそこに渡された変数以外どんなデ ータも読まないことを GCC へ知らせる。データは const 関数に渡されたポインタ を間接参照 dereference して読むことはできない。 pure または const 関数でプログラムが得られる唯一の結果はそれが値を返すこと だ。こうした関数はどこも指し示さない void を返す。 GCC ではその情報をありふれた下位の式の削除(!) を行なうのに使うことができる 。これはその結果が同じであることを知っているので毎回知らせるよりずっと少な い回数で関数を呼び出せることを意味する。例えば -- ある関数で "Celsious" を "Fahrenheit" に変換しそれが毎回同じ値を計算するループに置かれていたとすると GCC は関数の呼び出しをその戻り値と交換しようとする。 GCC は交換する関数が const であれば安全なことがわかっている。