C 言語のポインタについての基礎中の基礎について解説します。*1
ポインタに入る前に、先に通常の変数宣言について解説する必要があるので解説します。
例えば int 型の変数を使いたい場合は以下の用に宣言します。
int x = 10;
この用に記述したとき、下の図のようにメモリ領域に "x" という名前で確保されます。
アドレスが16進表記だったり 4 ずつ増加しているのは特に深い意味はありません。要するにメモリの領域には番号が振ってあるという事がポイントです。
今後、この変数は x という名前で使用できますね。例えば
x = 20;
と、記述した場合、メモリ領域は以下のような感じになります。
コンピュータではメモリにアクセスするにはアドレスを指定する必要があるのですが、そこに "x" という名前を付けて使えるようになるわけです。
ポイント
- 変数を宣言すると、メモリに領域が確保される。
- 以後は変数名でそのメモリ領域にアクセスできる。
ここで重要なのが、メモリを意識することです。変数を箱として考えると序盤は問題無く考えることができますが、 C 言語基本機能の配列、構造体あたりでイメージが食い違ってきます。
ここからはポインタについてです。ここでこの "x" というメモリ領域のアドレス(図でいう 0x1000)を取得する方法があります。それは以下のように記述します。
&x;
"x" に & 記号を前に付けるだけですね。これで変数 x におけるアドレス(図でいう 0x1000)を得ることができます。これは int とはまた違う型なので int の変数に代入はできませんワーニングとなります(個人的にはエラーでもかまわないぐらい危険な行為)。
int y; y = &x; // ワーニングとなる
ポイント
- 変数名の前に & を付けるとその変数(の宣言時に確保したメモリ)のアドレスが取得できる
それで、このアドレスを保存しておく変数も作ることができます。以下のように変数を宣言します。
int * p;
イメージとしては以下の図のようになります。
また、代入もすることもできます。
p = &x;
これのイメージは以下です。
そして、ポインタがあればそれが指し示す先にアクセスすることができます。つまりこの場合は x (と名前を付けたメモリ領域)に p を使ってアクセスすることができます。記述方法はポインタの前に * を付けます。
*p = 30;
この時のイメージは以下のようになります。
以上の解説では全て int 型で説明しましたが、これは全ての型でも応用することが可能で、例えば char や構造体などでも全く同じです。
ポイント
- 宣言時に * を使うことでポインタ型の変数を宣言することができる。
- 当然、その変数にはアドレス値を入れる事ができる
- ポインタ型の変数からそれが指し示す先にアクセスできる
以上がポインタの基礎中の基礎になります。基礎中の基礎なので、「これで何が良いの?」とか「どうやって使うのか?」とか「何が便利になるのか?」といった疑問が出てくると思いますが、それは他の解説書を読んでください。その際にこの基礎の事を念頭に読めば理解しやすくなると思います。
関数ポインタについて。まず、関数ポインタなんて応用中の応用なのでポインタを理解したばかりなら理解する必要ありません。なので、簡単に説明して後で必要になったときにちゃんと勉強すると良いでしょう。
よく使う関数として printf があると思います。使う際はこんな感じで記述すると思います。
printf( "Hello World\n" );
この printf ですが、実は関数名を書くだけで関数のアドレスが取得できるのですね。つまり
printf;
で関数のアドレスを表しているのです。それで、この関数名の後に括弧を付けると関数呼び出しとなるのです。
ここで、この関数のアドレスを保存できる変数 fp があるとした場合、以下のように関数のアドレスを代入することが出来ます。
fp = printf;
printf のアドレスが変数 fp に入っている訳です。それで fp を使って関数呼び出しをすることができます。
fp( "Hello World\n" );
これの他にも以下のような記述も可能です。両方とも同じ事になります。
(*fp)("Hello World\n" );
関数ポインタを使って呼び出しているということを明示するために後者の使い方で呼び出すことが多いようです。この変数 fp ですが、宣言するには色々と難しいです。これ以上は他の解説を見て勉強してください。
最後に。
どうですか?難しいですか?個人的には相当丁寧に解説したつもりですが(俗に言う優しくとか判りやすくではない)、あえて他の事(箱とかショートカットとか)で例えたりしないで出来るだけ「正確に」やってみました。何故そうしたのかというと、他の事で例えるとその例えた事では表現出来ないことをやろうとすると壁にぶつかるからなんですね。
C 言語は高級アセンブラと呼ばれる事があるように、コンピュータの仕組みとは切っても切れない縁にあります。コンピュータを扱う為に C 言語は存在しておりコンピュータの仕組みに直結した設計になっています。なので、「正しく」理解する為には絶対にコンピュータに仕組みの理解は不可欠です。もし、コンピュータの仕組みを理解せずに理解しようとしても、それは必ずどこか違う勉強すればするほど破綻をきたしてしまうでしょう。個人的には上記の説明で頭が爆発する人は、プログラミング(少なくとも C 言語)には向いてないです。坂道発進が出来ないのにマニュアル車を運転するのがダメなように、この程度の事が理解できないなら少なくとも C 言語は止めた方が良いです。他にも良い言語はたくさんありますよ。
※11/9 関数ポインタについて少し修正
※11/10 コンパイルエラー→ワーニングに修正。
※11/13 関数ポインタのところを修正。関数のアドレスが取得できるのを主とするようにした。
*1:ここでは初心者向けにある程度適度にはしょって説明しています。 C 言語仕様的には限定されない(例えばポインタはアドレスに限らない)のは判った上で初心者向けに限定して説明しています