C では、配列を関数に渡すと、配列はポインターに "減衰" します。この関数は、配列の最初の要素へのポインターを受け取ります。配列自体は受け取りません。呼び出された関数でポインターの割り当てに加えられた変更は、呼び出し元の関数には反映されません。
最初の例でtest()
は、文字列の配列を受け取り、関数内でそのポインターが指すものを変更します。したがって、 のローカル コピーはptr
、 とNULL
同様に、関数内で割り当てられたメモリを取得しptr[0]
ます。ただし、これらの変更は に対してローカルtest()
です。呼び出し関数には反映されません。test()
実行が終了して戻るときptr
、呼び出し関数の の値はまだNULL
です。で割り当てられたメモリにアクセスする方法がないため、メモリ リークが発生しますtest()
。
呼び出し元の関数に変更が反映されるようにするには、文字列の配列へのポインターを渡す必要があります。したがって、&ptr
呼び出しの と の定義の 3 レベル ポインターですtest()
。別のより簡単なアプローチは次のとおりです。
int main()
{
char **ptr= NULL;
ptr = test();
printf("%s", ptr[0]);
return 0;
}
char** test(void)
{
char **ptr = (char **) malloc(sizeof(char *));
ptr[0] = (char *) malloc(sizeof(char) * 5);
strcpy(ptr[0], "abc");
return ptr;
}
1 つの明確化: 「呼び出された関数のポインター割り当てに加えられた変更は、呼び出し元の関数には反映されません」と述べました。これは、「配列要素への変更は、呼び出し元の関数に反映されない」ということと同じではありません。このことを考慮:
int main (void) {
int array [] = { 0, 1, 2, 3, 4 };
test1 (array);
printf ("%d\n", *array);
test2 (array);
printf ("%d\n", *array);
return 0;
}
void test1 (int* array) {
int new_array[] = { 3, 4, 5, 6, 7 };
array = new_array;
return;
}
void test2 (int* array) {
array [0] = 5; // or *array = 5
array [1] = 6;
array [2] = 7;
array [3] = 8;
array [4] = 9;
return;
}
出力:
0
5
ここでtest1()
の変更は、配列ポインター自体が指している場所であり、呼び出し元には反映されません。そのため、呼び出し元の関数で*test
は 0 のままです。test2()
では、変更は配列要素に対して行われ、呼び出し元に反映されます。したがって、output: の違いは*test
5 になりました。これはすべて、動的メモリ割り当てではなく静的メモリ割り当てを使用しますが、原則は同じです。