C (および C++) のポインターは、正しく考えればそれほど難しくありません。
次のコードでデモンストレーションしましょう。
void foo(int i) {
i = i + 5;
}
int main()
{
int i = 5;
foo(i);
printf("i is %d\n", i);
return 0;
}
Q.呼ばれた後のiinの値はどうなりますか?main()foo()
A. iは に値渡しさfoo()れるため、iinmain()は によって変更されませんfoo()。iまだ5です。
それでは、コードを少し変更しましょう。
void foo(int* i) {
i = malloc(sizeof(int));
}
int main()
{
int *i = 0;
foo(i);
printf("i is %p\n", i); /* printf a pointer with %p */
return 0;
}
Q.呼ばれた後のiinの値はどうなりますか?main()foo()
A. iは に値渡しさfoo()れるため、iinmain()は によって変更されませんfoo()。iはまだ 0 です。
つまり、何も変わっていません!ポインタであることiは、値渡しであることには変わりありません。
実際、C では、すべての関数パラメーターは値渡しです。では、変数を変更する関数を取得するにはどうすればよいでしょうか。
関数に変数を渡して変更する場合は、その変数のアドレスを関数に渡す必要があります。(これは C にも当てはまります。C++ でも参照を使用できますが、ここではポインターについてのみ話します。)
変数のアドレスを渡すときは、次の 2 つのことを行います。
変数のメモリアドレスを計算しています
そのメモリアドレスを値で関数に渡しています。
メモリ アドレスを使用して、メモリ アドレスが指すメモリを変更できます。関数内のメモリ アドレスは関数呼び出しの外のアドレスと同じであるため (値渡しであるため)、それらが指す変数は同じです!
これは本当に最も難しい概念なので、ASCII を描いてみましょう。
| |
+------------+ <- 0x04762198
| |
| i |
| |
| |
+------------+ <- 0x0476219C
| |
を紹介しますint i。iは、メモリ アドレス から始まる 4 バイト (このシステムでは)0x04762198です。すべての変数はメモリ内のどこかに保存され、このようなメモリ アドレスになります。
に値を割り当てるとi、その値は上記のメモリ ブロックに格納されます。
関数に渡すiと、関数で使用するために、の値がiメモリ内の別の場所にコピーされます。そのメモリの値は元の と同じになりますiが、その変数のメモリ アドレスは別の場所になります。
ここに独創的なビットがあります。代わりに関数に渡す0x04762198と、その関数は元のi!のメモリ位置にアクセスできるようになります。これはポインタであり、メモリ内のアドレスを指すことからそう呼ばれています。iポインターを使用して関数内のオリジナルを変更したい場合は、それを逆参照します (例: *ptr = 5;)。私たちが実際に行っているのは、 「この値 (5) を が指すメモリに格納してくださいptr」 ) ということです。
これを実装するためにコードをもう一度変更しましょう。
/*
* The address of an int* is int**
*/
void foo(int** i) {
/* dereference to access and modify the original `i` */
*i = malloc(sizeof(int));
}
int main()
{
int *i = 0;
/*
* Pass the address of i, so foo() can modify i
*/
foo(&i);
printf("i is %p\n", i); /* printf a pointer with %p */
return 0;
}
違いを見ます?
さて、自分のプログラムで何が間違っているかがわかりますか?
注: 簡潔にするために、通常のエラー チェック (たとえば、malloc() が NULL を返さないことのチェック) は省いています。