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.呼ばれた後のi
inの値はどうなりますか?main()
foo()
A. i
は に値渡しさfoo()
れるため、i
inmain()
は によって変更されません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.呼ばれた後のi
inの値はどうなりますか?main()
foo()
A. i
は に値渡しさfoo()
れるため、i
inmain()
は によって変更されません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 を返さないことのチェック) は省いています。