Dokusyo-nissi Bessitu 2007-06-06 φ(-_-) ■[lang]はじめての C [Learning GNU C] 4.3. ループ ループはコマンドをくり返す方法を備えそれが何回くり返されるかを調整する。例 えばスクリーン上にアルファベットを表示したいときは printf() を呼び出すこと で実行できる。これも 1つの答だがそれではあまりうまくは拡張 scale されない。 仮りに 1行で 1 から 1000 までの数を全て表示したいとすると巨大な 1つの printf() か多数の printf() を呼び出して処理されたとしてもそれぞれの仕事をコ ンピュータに実行させるためにはプログラムの中の興味のある箇所にかかわるため の多くの時間が失われてしまう。 4.4. while もっとも基本的な C のループが while ループだ。 while 命令文は if 命令文のく り返しのようなものだ。 if 命令文と同じく条件文 test condition が真であれば 命令文が実行される。異なるのは命令文が実行された後に再び条件文がチェックさ れることだ。それがなお真ならば再度命令文が実行される。このサイクルは条件文 が偽だと評価されるまでくり返す。初めの時点で条件文が偽ならば命令文はまった く実行されない。その一方もし条件文が絶対に偽と評価されなければ loop は永遠 に続くだろう。その code でループが実行される回数を調整するには条件文の中に それに続く code のブロックで変化する変数を通常少なくとも 1つもつことだ。こ れがある時点で条件文が偽となるようにする。 これが期待されているだろう即製の例題だ。それはある人が非常に簡単な暗号 code を書きその答を求めるという単純なゲームだ。このプログラムを試してみるときは 何回かまちがった答が引き出されるの覚えておくこと。 Example 4-3. guess_my_number.c int main() { const int MAGIC_NUMBER = 6; int guessed_number; printf("ぼくの 考えてる 数が なにか 当ててみて\n"); printf("ヒント -- それは 1から 10 までの 数\n"); printf("じゃ 入力してみて: "); scanf("%d", &guessed_number); while (guessed_number != MAGIC_NUMBER) { printf("残念、もう 1回: "); scanf("%d", &guessed_number); } printf("当たり!\n"); return 0; } while 命令文の後の code のブロックはプレーヤーが数を '6' だと判断するまでく り返し実行される。 2007-06-09 φ(-_-) ■[lang]はじめての C [Learning GNU C] 4.5. for for は while と似ていてその書き方が異なる。 for 命令文は数を並べるようなリ ストでの処理によく使われる。 Example 4-4. for_ten.c #include int main() { int i; /* 0 から 9 までの 数を 表示 */ for (i = 0; i < 10; i++) printf("%d\n", i); return 0; } 4.6. do .. while do .. while は条件文を最初ではなくループの最後でチェックする以外は while と 同じだ。これはループの内容 content が少なくとも 1回は実行されるという意味だ 。 Example 4-5. guess_my_number2.c #include int main() { const int MAGIC_NUMBER = 6; int guess_number; printf("ぼくの 考えてる 数が なにか 当ててみて\n"); printf("ヒント -- それは 1 から 10 までの 数\n"); do { printf("じゃ 入力してみて: "); scanf("%d", &guess_number); } while (guess_number != MAGIC_NUMBER); printf("当たり!\n"); return 0; } 4.7. switch switch 命令文はほぼ入れ子 nest になった if ... else のようなものだ。それは 主に使う側の好みの問題であり switch 命令文はほんの少し効率的で読みやすい。 2007-06-11 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第5章ポインタ 学生からプログラマを区分 sort するもの。 5.1. 基本 関数はその返り値を通してのみプログラムに作用するという 1つの制限に気づいた だろう。そこで関数を変数よりもっと効果のあるものとしたい場合どうすればいい のか? ポインタを使うことだ。ポインタは特別な種類の変数だ。ポインタはメモリ アドレス、つまり別の変数のアドレスを記憶するよう設計されている。ポインタの 宣言はその変数の識別子 (名前) の前にアスタリスク '*' をつける他は普通の変数 の宣言と同じだ。ポインタを使って作業する上で知っておく必要のある 2つの新し い演算子がある。 "address of" 演算子 '&' と間接演算子 deferencing operator の '*' だ。 2つとも前置単項演算子 prefix unary operator だ。変数の前にアン ド ampersand '&' をつけることでそのアドレスを得てこれをポインタに記憶させる ことができる。 (また) ポインタの前にアスタリスク '*' をつけることでそれが指 し示す point メモリアドレスの値が得られる。いつものようにこれがどれほど簡単 かを示す即製の例題を見よう。 Example 5-1. pointers_are_simple.c #include int main() { int my_variable = 6, other_variable = 10; int *my_pointer; printf("the address of my_variable is : %p\n", &my_variable); printf("the address of other_valuable is : %p\n", &other_valuable); my_pointer = &my_variable; printf("\nafter \"my_pointer = &my_variable\":\n"); printf("\tthe value of my_pointer is %p\n", my_pointer); printf("\tthe value at that address is %d\n", *my_pointer); my_pointer = &other_variable; printf("\nafter \"my_pointer = &other_variable\":\n"); printf("\tthe value of my_pointer is %p\n", my_pointer); printf("\tthe value at that address is %d\n", *my_pointer); return 0; } 出力は 2つの変数のアドレスを表示する。あなたのシステムが変数に割り当てたア ドレスは私のものとは異なるはずだ。 printf() ではアドレスを表示するため "%p" を使うことに注意。これは全てのポインタに対する変換指定子だ。私が得た出力で は常にこうなる。 the address of my_variable : 0xbffffa18 the address of other_variable : 0xbffffa14 after "my_pointer = &my_variable": the value of my_pointer is 0xbffffa18 the value at that address is 6 after "my_pointer = &other_variable": the value of my_pointer is 0xbffffa14 the value at that address is 10 ほら、これはそれほど複雑ではない。一度ポインタに親しめば C の習得はずっと前 へ進む。 2007-06-13 φ(-_-) ■[lang]はじめての C [Learning GNU C] 5.2. 変数のアドレス プログラムを動かしていて変数の宣言に出会うとプログラムはいくらかのメモリを 要求する。 OS は十分な大きさの空いたメモリのスペースを捜してそのメモリスペ ースのアドレスをプログラムに知らせる。プログラムはこの変数に保持されたデー タを読みたいときはいつでもそのメモリアドレスを調べ変数のデータ型の大きさに 等しい分の byte 数を読む。 この章の始めにあった例題を実行すると 2回目にはアドレスは同じ結果を得るかも しれないしそうでないかもしれない。それはシステム次第だがたとえ今は再度正確 に同じアドレスを得たとしても明日は同じ結果が得られる保証はない。実際どちら かといえばそれはあり得ない。 2007-06-15 φ(-_-) ■[lang]はじめての C [Learning GNU C] 5.3. 関数の引数 argument としてのポインタ ポインタのすばらしいところの 1つが変数を -- それ自身の有効範囲 scope の外側 から -- 関数により交換 swap させられることだ。関数にポインタを渡すことで変 数が保持するデータの読み/書きが可能になる。例えば関数を書いて 2つの変数の値 を交換することができる。ポインタがなければそれは実際不可能だ。どうポインタ を使うかがこれだ。 Example 5-2. swap_ints.c #include int swap_ints(int *first_number, int *second_number); int main() { int a = 4, b = 7; printf("pre-swap values are: a == %d, b ==%d\n", a, b); swap_ints(&a, &b); printf("post-swap values are: a == %d, b == %d\n", a, b); return 0; } int swap_ints(int *first_number, int *second_number) { int temp; /* temp = "what is pointed to by first_number; etc ... */ temp = *first_number; *first_number = *second_number; *second_number = temp; return 0; } 見てのとおり swap_ints() を宣言して 2つのポインタ (変数のアドレス) の要求を GCC に知らせる。またその値に代わって演算子 (&) のアドレスが swap_ints() で 読まれ 2つの変数のアドレスを渡すのに使われている。 2007-06-16 φ(-_-) sekiyo2007-06-16 ■[lang]はじめての C [Learning GNU C] 5.4. ポインタ演算 arithmetic ちょうど他の変数のようにポインタでも演算を行なうことができる。これはほんの わずかな場合でしか役にたたない。 (なんらかの理由から) ポインタを 2つに分け たりするとおそらくそれはそのプログラムが属していないコンピュータのメモリ領 域を指し示す point to ことになる。プログラムがこのメモリ領域で読み/書きを試 みると "segumentation fault" という text が表示されてプログラムは中断される だろう。プログラムがアクセスの許可されないメモリの部分にアクセスを試みたと き "segumentation fault" は起こる。 しかしポインタは簡単な加算 addition に使えるときがある。これは次の章で配列 array (連続したメモリアドレスをもつ多項 multiple 変数) を検討するときに見て みよう。加算 (それに減算 subtraction) するとき演算はポインタのデータ型と等 しい単位で行なわれる。 5.5. 汎用 generic ポインタ void 型のポインタで変数を宣言するとそれは汎用ポインタとして認識される。 (実 際には) void 型の変数をもつことはできないのでポインタはどんな型も指し示さず そのため参照 dereference ができない。それでもポインタなので使う場合は最初に それを他の種類のポインタの型に当てはめて cast おかないといけない。このため 汎用ポインタと表される。 これはポインタが異なった時に異なった型のデータを指し示す場合にとても役だつ 。 これが void ポインタを使った code だ。 Example 5-3. generic_pointer.c #include int main() { int i; char c; void *the_data; i = 6; c = 'a'; the_data = &i; printf("the_data points to the integer value %d\n", *(int *) the_data); the_data = &c; printf("the_data now points to the character %c\n", *(char *) the_data); return 0; } 2007-06-17 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第6章構造化データ structured data の型 連続的 contiguous 構造化データ型のこと 6.1. 構造化データ型とはなにか? C では構造化あるいは総合化 aggregate データの型を決めるのに 2つの方法がある 。どちらも -- 別の構造体 struct や配列 array へのポインタを含む -- どんな標 準的 standard データ型からでも構成することが可能だ。構造体が (異なった) 型 を混ぜ合わせて入れることができるのに対し配列は多くの同じ型をもつ変数からな る。 6.2. 配列 array 配列は多くの変数を同時に含んだデータ型だ。配列のそれぞれの要素 element には その要素にアクセスできるよう番号 number が与えられる。 100 の要素からなる配 列では 1つ目の要素が 0 (ゼロ) で最後が 99 だ。こうした指数 index によるアク セスは配列のそれぞれの要素を渡り歩くのに都合がいい。 6.3. 配列の宣言とアクセス 配列の宣言は配列の大きさを指定しないといけないこと以外はその他の変数の宣言 とほとんど同じだ。その大きさ (または要素の数) が配列識別子の後のカクカッコ "[ ]" に置かれた整数値だ。 Example 6-1. first_array.c #include int main() { int person[10]; float hourly_wage[4] = {2, 4.9, 10, 123.456}; int index; index = 4; person[index] = 56; printf("the %dth person is number %d and earns $%f an hour\n", (index + 1), person[index], hourly_wage[index]); return 0; } 注意点: 確かめるのはかまわないがその配列にはない要素、例えば 10 の要素から なる配列の 11番目の要素へはアクセスを試みない。配列の限界を越える値にアクセ スを試みるとプログラムを壊してしまうかもっと悪くてエラーが起きた警告無しに ゴミ garbage データを取り込んでしまうかのどちらかになるだろう。 2007-06-20 φ(-_-) ■[lang]はじめての C [Learning GNU C] 6.4. 配列の初期化 initialize 先ほどの例題では配列 "hourly_wage" をコンマで区切られた値を中カッコ "{ }" に置くことで初期化した。この方法を使えば多少にかかわらず配列の要素を思うよ うに初期化できるが始めに全ての要素を初期化する以外は要素を初期化することが できない。仮りに全ての配列の要素でなくそのうちのいくらかを初期化すると残り の要素は自動的にゼロに初期化されるだろう。 この不具合を避けるため GNU の C language への拡張では番号 (指数) を選択して 配列の要素の初期化ができる。番号によって初期化するときはどのような順序であ れ要素を前もって [index] = value とすることで中カッコに置くことができる。こ ういうふうに、 Example 6-2. initialize_array.c #include int main() { int i; int first_array[100] = {[90] = 4, [0] = 5, [98] = 6}; double second_array[4] = {[3] = 1.01, [4] = 1.02}; printf("sure enough, first_array[90] == %d\n\n", first_array[90]); printf("sure enough, first_array[99] == %d\n\n", first_array[99]); for (i = 0; i < 5, i++) printf("value of second_array[%d] is %f\n", i, second_array[i]); return 0; } 2007-06-24 φ(-_-) ■[lang]はじめての C [Learning GNU C] 6.5. 多次元 multidimensional 配列 先の例題で使われた配列は 1次元配列だった。配列は次元を 1つ以上もつことがで きる。こうした配列の配列 array-of-array は多次元配列と呼ばれる。それは配列 の識別子 (名前) の後に複数のカクカッコ "[ ]" のセットをもつことを除けば基本 になる配列と全く同じだ。 2次元の配列は列 row と行 column の網目 grid のよう に考えることができる。 Example 6-3. number_square.c #include const int num_rows = 7; const int num_columns= 5; int main() { int box[num_rows][num_columns]; int row, columns; for (row = 0; row < num_rows; row++) for (column = 0; column < num_columns; column++) box[row][column] = column + (row * num_columns); for (row = 0; row < num_rows; row++) { for (column = 0; column < mum_columns; column++) { printf("%4d", box[row][column]); } printf("\n"); } return 0; } この例題をコンパイルし実行すればこのような数字の長方形 box が得られるだろう 。 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 上の配列は 2つの次元をもち 2組添字つき double subscripted 配列と呼ぶことが できる。 GCC では 3次元以上の配列を使うことは実際には非常にまれだとしても 29次元までの配列が可能だ。 6.6. 文字配列 (文字列) C の文字列 text はナル null 文字 '\n' で終わる char 型変数の連続した number として (配列あるいはポインタで) 表される。 6.7. データ型の定義 C language にはもっとも基本的でよく使われる型しか用意されていない。多くの language ではもっと大きな型のセットが用意されているがそれは便宜上のためでし かない。 text 文字列を扱う C での方法がこのよい例だ。時には複雑な data を保 持できる別なデータ型があれば役立つと考えるかもしれない。 C では (それを) 自 分自身で決めるようにする。 2007-06-28 φ(-_-) ■[lang]はじめての C [Learning GNU C] 6.8. 構造化データ structured data C で新しい、例えば "Person" という型をつくることができる。 "Person" へは "name" という文字列、"age" という int (型) それに "height_in_cm" という別の int を記憶させられる。これが新しい型でつくった code だ。 struct Person { char *name; int age; int height_in_cm; }; この code から "struct Person" という変数がつくられる。いつものようにこの型 に入っている変数と変数のポインタをつくることができる。さて、"struct Person" 型の変数 "sidekick" を宣言した。そこの "age" 領域 field へアクセスするには "sidekick.age" が使われている。先の定義 "struct Person" を使った即製の例題 でこれらのことをはっきりさせよう。 Example 6-4. person_struct.h and person_struct.c struct Person { char *name; int age; int height_in_cm; }; #include #include "person_struct.h" int main() { struct Person hero = {"Robin Hood", 20, 191}; struct Person sidekick; sidekick.name = "John Little"; sidekick.age = 31; sidekick.height_in_cm = 237; printf("%s is %d years old and stands %dcm tall in his socks.\n", sidekick.name, sidekick.age, sidekick.height_in_cm); printf("He is often seen with %s.\n", hero.name); return 0; } コンパイルし実行するとこのように表示される。 John Little is 31 years old and stands 237cm tall in his socks. He is often seen with Robin Hood. 6.9. 共用体 union C はまた動的 dynamic な型をもつことのできる型にも対応 support していて、1つ の変数をある時点で "int" に、次に "double" として、その後に "unsigned long long" へすることができる。こうした data の型は "union" というキーワードを使 う以外は構造体と同じように宣言される。 (ただし) その作動 behavior は構造体 とは全く異なる。 2007-07-02 φ(-_-) ■[lang]はじめての C [Learning GNU C] 第7章実行時 run-time のメモリ割り当て memory allocation (プログラムの) 実行中にメモリを要求すること。 7.1. なぜそれは必要か。 プログラムを書くときたいていどのくらいのデータを記憶あるいは処理しないとい けないのかを実際は知っていない。以前の例題ではユーザからのデータをある程度 読み込んだ。データを記憶するため大きな文字配列を使ったがユーザがそこで扱え るより多くの text を書き込むとなにが起きるか? プログラムは壊れ、ひどいこと になる。(そのため) アプリケーションの実行時により多くのメモリを要求すること ができる。 7.2. 動的 dynamic なメモリ関数 glibc にはメモリを余分に要求するための関数が用意されていてそこで初めに説明 されるのが malloc() だ。 (この関数を) 開始するためにはポインタが必要になる 。 7.3. 実行 (ランタイム) メモリの要点 プログラミングで可能性がある最悪の間違いの 1つが終了時にメモリを開放 free() するのを忘れることだ。 (では) ポインタを見失うことはよくある不具合 problem か? 領域 scope の外ではポインタは失効するのか? 第8章文字列とファイルの出入力 I/O ファイルの読み書きのこと。 8.1. はじめに holder をセットする??