ポインターに入門
通称 K&R を読みながらの C 言語の独学ですが、第 5 章 「ポインタと配列」 は若干ページ数が多く、まだ読み終えられていません。 読んでいてよくわからない部分も少なくありません。 が、それでも、いまのところ退屈せずに読み続けることができています。
C 言語の入門者が最初につまずくところと言われるポインターの話題は、たしかに難しいと感じます。 が、それは、ポインターとは何かということ自体の難しさではなく、それを利用したプログラムの記述の仕方やちょっとしたテクニックが、慣れないうちは覚えにくい、というところに理由があるのではないかと思います。 意味不明でまったく理解ができない、という類の問題ではないような気がしています。 ただ、同時に、うっかり間違えてしまう or 誤解してしまうような落とし穴も少なくないようには感じますが。
感心したこと
ところで、この章では、「5.3 ポインタと配列」 で、「へえ、そうなんだ」 と少し感心する部分がありましたので、そのことを書きとめておこうと思います。
仮に int
型のデータを要素とする配列の名前を data
とします。 また、int
型のデータを保持する変数のポインターを p
とします。 で、次のようなコードを書いたとします。
#include <stdio.h> main() { int data[] = { 1, 10, 100, 1000, 10000 }; int *p; p = &data[2]; printf("%5d\n", *p); printf("%5d\n", *(p + 1)); printf("%5d\n", *(p + 2)); }
これの出力結果は、次のようになります。
100 1000 10000
p = &data[2];
により、p
は data[2]
のポインターとなります。 したがって、printf("%5d\n", *p);
が 100
を出力するのは、わかります。
わたしが感心したのは、その次です。 すなわち、*(p + 1)
が data[3]
を表わし、*(p + 2)
が data[4]
を表わすという点です。 なぜ感心したかというと、たとえばわたしの環境では int
型のデータは 4 バイト (32 ビット) を必要とするので、data[3]
を表わすためには *(p + 4)
としなければならないんじゃないかと最初は思っていたからです。
ところが、p
は int
型のデータを保持する変数のポインターであることが宣言されているので、コンパイラーは p + 1
という演算において、ポインターを自動的に 4 バイト先に進めてくれるのですね。 この親切さに、まずは感心しました。
もうひとつ感心したこと
感心した点は、ほかにもあります。 それは、ポインターを順々に進めて行くことで、配列を順々に走査することができるという点です。 配列というものは、メモリ上では、ひとかたまりの場所に、添え字の順番に領域を確保してくれているのですね。 当たり前のことなのかもしれませんが、ちょっと感心しました。 もし、たとえば data[0]
の値は 1000
番地に格納され、data[1]
は 3c00
番地に、data[2]
は 2ffc
番地に、といった具体にバラバラに配置されていたら、上記のようなことはできなかったでしょう。
ちょっとよくない書き方なのかもしれませんが、このことについては、以下のコードで、自分なりに納得しました。
#include <stdio.h> main() { int data[] = { 1, 10, 100 }; int *p0, *p1, *p2; p0 = &data[0]; p1 = &data[1]; p2 = &data[2]; printf("%8x\n", (int)p0); printf("%8x\n", (int)p1); printf("%8x\n", (int)p2); }
よくない書き方かもしれないと思うのは、ポインターの値 (つまりメモリ上の番地となりましょうか) を、わたしの勝手な判断で int
型にキャストしている点です。 ポインターの値を勝手に整数に変換することが許されるのかどうか。 (ちなみに、64 ビット環境なら long
もしくは long int
にキャストするということになるのでしょうか。)
その点は脇に置いて、上記のコードを実行しますと、わたしの環境では、次のような出力結果を得ました。
22fef8 22fefc 22ff00
これが意味するのは、わたしが勝手に納得しているところによれば、data[0]
、data[1]
、data[2]
の各値の格納される番地は順番に連続して 4 バイトずつ並んでいる、ということなんだろうと思います。
ふと思いました。 Java で、オブジェクトを指す変数は実体はポインターに過ぎなくて、メソッドの引数や戻り値として使われる場合は 「参照渡し」 に過ぎなくて、とか、そういう言い回しに、初心者の頃は意味がよくわからず苦労した覚えがあります。 先に C 言語におけるポインターのことを少しでも学んでおけば、もっとすんなり理解することができたかもしれないなあと思うのです。
まとめ
K&R の 第 5 章 「ポインタと配列」 は、このあと、ポインターの配列や、ポインターへのポインターや、関数へのポインターなど、だんだん少しずつ話題が複雑になっていきます。 掲載されているサンプルコードを読み解くのに、だんだん時間がかかるようになってきます。 きちんと理解しきれているかどうか自信はありません。
それでも、なんとなくですが、このまま C 言語の基本を勉強していくことについて、ある程度は挫折せずに行けるところまでは行けるんじゃないか、そんな不思議な自信というか希望が湧いてきています。 とりあえず、もう少し進んで、このあとは第 6 章 「構造体」 が楽しみになってきました。
以上、今夜は自己満足な日記でした。