C 言語に、realloc という関数があります。この関数の使い方を間違っている(と、私には思われる)例を、よく見かける…というか、間違って使っている例しか見たことがないので、「今更 C 言語使う人いるの?」と思いながらも、書いてみる。
/* 間違っている例 */
...
char* ptr = NULL;
...
ptr = (char*) malloc(needSize);
if (ptr == NULL) { return; }
...
while (何かの条件) {
ptr = (char*) realloc(ptr, newNeedSize);
if (ptr == NULL) {
goto 適切に終了処理をして戻る;
}
...
}
...
適切に終了処理をして戻る:
if (ptr != NULL) {
free(ptr);
ptr = NULL;
}
...
何が間違っているのか。
realloc で NULL が返ってくることがあり得る。だからチェックしている。それだけでいい?
realloc は、第一引数で与えられたポインタが指している場所から確保されている領域を、第二引数で指定されたサイズに変更します。サイズの変更が拡大方向の場合、第一引数で指定された場所から新しいサイズを確保できない場合があります。このとき、新しいサイズが確保できる場所が、関数の戻り値となります。realloc 内部で第一引数で指定したポインタが指している内容を、元のサイズまでコピーしてくれます。そして、第一引数が指していた場所は、解放されます。
問題は、第一引数の場所を拡張できず、新しい場所も確保できなかった場合です。この場合、realloc は NULL を返します。そして、第一引数で指定したポインタは、解放されません。
つまり、上の「間違っている例」では、「goto 適切に終了処理をして戻る;」へ行った時点で、元の領域がリークします。
/* より適切な例 */
...
char* ptr = NULL;
...
ptr = (char*) malloc(needSize);
if (ptr == NULL) { return; }
...
while (何かの条件) {
char* dummy = (char*) realloc(ptr, newNeedSize);
if (dummy == NULL) {
goto 適切に終了処理をして戻る;
} else {
ptr = dummy;
}
...
}
...
適切に終了処理をして戻る:
if (ptr != NULL) {
free(ptr);
ptr = NULL;
}
...
もっとも、この直後にプログラムを終了させるなら、リーク、リークと騒ぐほどのものでもないと思います。またこの仕様は、処理系の実装によって異なっているかもしれません。この情報は、1990年代の Sun Microsystems 社製コンパイラ、および Microsoft 社製 Visual Studio 2005 に付属の解説を元にしています。
あ、コード中、ラベルに日本語文字を使用しているところは、大目に見てください(^-^;
投稿日時 : 2008年12月16日 23:29