答え
free()
とprintf()
の順番が間違い!
if (pCopyOfText) {
strncpy(pCopyOfText, text, sizeof(text));
free(pCopyOfText);
}
printf("%s\n", pCopyOfText);
printf()
をfree()
の前に実行するのが正解!
if (pCopyOfText) {
strncpy(pCopyOfText, text, sizeof(text));
printf("%s\n", pCopyOfText);
free(pCopyOfText);
}
解説
これは、ダングリングポインタに関する出題でした。ダングリング(dangling)は「宙ぶらりんの」という意味で、ポインタが有効なアドレスを指していない状態を表しています。
だいたいの場合、ダングリングポインタはメモリーを解放するタイミングで発生します。今回のプログラムでは、malloc()
で確保したメモリーをfree()
で解放していますね。このとき、ポインタが解放済みの領域を指したままになっています。
このポインタを、そのままprintf()
のところで使おうとしているのが問題です。もう無効になったアドレスを指しているため、何が起こるか分かりません。たまたま期待どおりの結果が表示されるかもしれないし、おかしな値が表示されるかもしれないのです。最悪の場合は、プログラム自体がクラッシュします。
解放済みのメモリーは、うっかり使わないように気を付けないと危険ってことね。
それって、気を付けようがないんじゃ……。
大丈夫。ポインタをきちんとクリアするクセを付ければいいのよ。
free()
でメモリーを解放したあと、そのメモリーを指すポインタを使い続けるのは危険です。それなら、ポインタの値をクリアしてしまいましょう。
free(pCopyOfText);
pCopyOfText = 0;
こうしておけば、ポインタが「宙ぶらりん」になるのは一瞬だけです。クリア済みのポインタは、うっかり使ったとしても確実にクラッシュさせることができます。
ええっ!クラッシュしないほうがいいんじゃないんですか?
それはそうよ。でも、バグがあるのにうまく動いてしまったら、なかなか問題に気付けないでしょう?
あ、もしかして……。クラッシュさせたほうがバグに気付きやすくなるってことですか?
そう!そうしたら早めに修正できるから、最終的にはクラッシュしにくいプログラムになるわね。
そういうことか〜。
ここがポイント!
メモリーを解放したら、ポインタも一緒にクリアするクセを付けよう!
修正後のプログラム
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char text[] = "Hello!";
char *pCopyOfText = malloc(sizeof(text));
if (pCopyOfText) {
strncpy(pCopyOfText, text, sizeof(text));
printf("%s\n", pCopyOfText); /* ← この行を修正 */
free(pCopyOfText);
pCopyOfText = 0; /* ← ポインタをクリア */
}
return EXIT_SUCCESS;
}
実行結果
Hello!