新年初のブログの記事はあんな感じでしたけど、今回は初心に戻ってといった感じでC/C++のポインタについて。
いわゆるC/C++の初心者のつまずきどころでしょうか。みんな一様に口をそろえて「ポインタが・・・」といいますよね。
ポインタっていったい何が難しいのでしょうか。
きっと、C/C++で初めて「変数や関数のメモリへの配置」というものを意識しなければならないからではないでしょうか。
もちろん、「関数に引数を渡すときに実引数として渡すためのもの」といった本質的ではない理解の仕方もあるのかもしれません。(もちろん間違いではありません)
ただ、本質的にはポインタは指定されたメモリアドレスに格納された『値』の『参照』です。
『参照』というのは実際にプログラミングの用語として存在するのですが、一体何を参照するかというと、指定されたメモリアドレスに格納されている『値』を『参照』します。
『値』というのは色々あって、変数、関数、またポインタである可能性もあります。
ところで、最初に言った「変数や関数のメモリへの配置」とは何でしょう。
プログラムは一時的な記憶領域として、『メモリ』というものを用います。そこには番号が付いていて、たとえば
int x;
と宣言したとしましょう。
するとプログラムはこの x という変数の値をメモリに入れておきます。(現時点では初期化していないので、よくわからない値が入っています。)
そしてプログラムはそれ以降、この x という変数をメモリ上のアドレスを頼りに使います。たとえば
x = 0;
と言う風に x が使われた場合は、x の中身の値が入っている(はず)のメモリアドレスの部分を 0 で上書きします。
これは単純な変数の場合ですが、次は本命のポインタです。
ポインタは「自分の持っている値と一致するメモリアドレスに入っている『値』を参照する」変数です。ここで『値』と言っているのは先ほど言った通り、変数(たとえば先の x )でも関数でも、ポインタでもいいです。
たとえば次のようなプログラムを考えます。
int x = 0; int *y; // ポインタ y の宣言 y = &x; // &x は x のアドレスの値 printf("x = %d\n", x); printf("*y = %d\n\n", *y); x = 1; printf("x = %d\n", x); printf("*y = %d\n\n", *y); *y = 2; printf("x = %d\n", x); printf("*y = %d\n", *y);
これは次のような結果を出力します。x の値を書きかえると、*y の値も書き変わることが分かります。また逆も同じように書き変わっています。
x = 0 *y = 0 x = 1 *y = 1 x = 2 *y = 2
ところで、なぜ関数の引数に渡した変数を関数内で書き変えたいとき、実引数で(つまりポインタを使ってアドレス参照で)渡す必要があったのでしょうか。(ここまでくればわかるでしょうか。)
これは簡単に言うと、関数を呼び出したとき、引数(という変数)に渡した変数の『値』だけをコピーしているからです。もちろん引数は渡した変数とは違う形でメモリ上に配置されているので、渡した変数の値が書き変わることはありません。
そして、関数の終了と同時に引数は生存期間を終え、メモリ上から消えます。
すると、渡した変数はもちろん変化しません。紙をコピーして、そのコピーしたものを書き変えても、元の紙に書かれたことは変化しないというのと同じです。
そこで、ポインタが使えるわけです。
ポインタはメモリ上のアドレス(たとえば変数 x のアドレス)を渡されれば、そのアドレスに格納された値を参照します。
ポインタで見えている値を書きかえれば、ポインタの持っているアドレスの部分にある値(変数 x の値)が書き変わるのです。
とここまで書いていて思ったのは、私は基本的なポインタについての知識は大丈夫そうだということ。
この記事を書こうと思った理由は、ポインタについてちゃんと理解しているかを確認するためでした。
実際にここまで書ければ問題はないかな。
これで間違ってたら泣く。