関数を呼び出す側でポインタ変数を宣言し、そのポインタ変数を渡した関数の内部でmalloc()やrealloc()といった関数でメモリの確保や再確保を行う場合には、注意が必要です。
というのは、malloc()やrealloc()の実行後、ポインタの値が変わってしまうということです。つまり、ポインタ変数の保持しているアドレスが変わるということです。これだけではなぜ重大な問題なのかわからないかもしれませんが、よく考えてみてください。関数内で宣言したポインタ変数のスコープは関数内だけであり、関数が終了すると破棄されてしまうのです。つまり、せっかく関数内で確保したメモリ領域も、その場所を示すべきポインタの値があてにならないので、アクセスできないということになります。これを示したのが次のプログラムです。
#include <stdio.h>
#include <stdlib.h>
void alloc(char* buffer)
{
printf("関数呼び出し時:%p\n",buffer);
buffer = (char*)malloc(1000);
printf("関数終了時 :%p\n",buffer);
}
int main(void)
{
char* buffer;
printf("宣言直後 :%p\n",buffer);
alloc(buffer);
printf("main()復帰時 :%p\n",buffer);
return 0;
}
Windows2000上で、Borland C++ 5.5.1を使って試しに実行すると、次のような結果になりました。printf()の%p指定は処理系依存ですが、たいていの処理系ではポインタ変数が保持しているアドレスを16進数で表示します。
main()内で宣言したポインタ変数の値が、関数に渡してmalloc()を実行した結果、変化していることがわかるでしょう。また。関数を終了してmain()に戻ると、元の値に戻ってしまっています。
では、関数内で変化したポインタの値をmain()にも反映させる方法はないのでしょうか。そんなことはありませんね。まず正攻法として思いつくのが、関数の戻り値としてポインタの値を返す方法です。早速やってみましょう。
#include <stdio.h>
#include <stdlib.h>
char* alloc(char* buffer)
{
printf("関数呼び出し時:%p\n",buffer);
buffer = (char*)malloc(1000);
printf("関数終了時 :%p\n",buffer);
return buffer;
}
int main(void)
{
char* buffer;
printf("宣言直後 :%p\n",buffer);
buffer = alloc(buffer);
printf("main()復帰時 :%p\n",buffer);
return 0;
}
これで一応はうまくいっていますね。ただ、この方法では、関数の値として別のものを返したい場合や、関数内で複数の領域を確保する場合に困ります。そこで考えられるのが、ポインタを直接渡すのではなく、ポインタへのポインタを使って、ポインタ変数自体が使っているメモリアドレスを渡すという方法です。その例を次に示します。
#include <stdio.h>
#include <stdlib.h>
void alloc(char** buffer)
{
printf("関数呼び出し時:%p\n",*buffer);
*buffer = (char*)malloc(1000);
printf("関数終了時 :%p\n",*buffer);
}
int main(void)
{
char* buffer;
printf("宣言直後 :%p\n",buffer);
alloc(&buffer);
printf("main()復帰時 :%p\n",buffer);
return 0;
}
この方法では、呼び出し側から関数に引数としてポインタ変数のアドレスを渡し、関数内では間接参照を用いて扱っています。こうしておけば、関数内でポインタ変数の値が変わってしまっても、呼び出し側でも同時に変わっているので問題ありませんね。