(error) Common realloc mistake: ‘p’ nulled but not freed upon failureera
エラー:reallocに失敗したが古い領域は解放されていない[(error)memleakOnRealloc]
■この記事の概要
この記事は、realloc関数を使用する際のよくあるミスによるメモリリークについて詳しく解説しています。
特に、reallocの戻り値を同じポインタに割り当ててしまう問題や、メモリ領域を手動で解放する際の誤りに焦点を当て、それらの問題を回避するための適切なコーディング方法を紹介しています。
C言語のメモリ管理において安全で効率的な実装を目指す方に有用な情報です。
■reallocの基本的な使い方
reallocとは
ラーメンを頼んだ後で
食べ足りないので追加の替え玉を頼むように、
mallocした後でメモリが足りないので
追加のメモリをOSに頼む時に使います。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char *oldp = malloc(strlen("ラーメン")+1);
if(oldp == NULL){
perror(NULL);
return 1;
}
strcpy(oldp,"ラーメン");
printf("旧:%s\n",oldp);
char *newp = realloc(oldp,strlen("ラーメン")+strlen("+替え玉")+1);
if(newp == NULL){ //新領域確保失敗
perror(NULL);
free(oldp); //旧領域はまだ有効なので解放する
return 1;
}
strcat(newp,"+替え玉");
printf("新:%s\n",newp);
free(newp);
}
./a.out
旧:ラーメン
新:ラーメン+替え玉
■新旧ポインタが同じだと失敗時旧領域を解放できなくなる
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char *p = malloc(strlen("ラーメン")+1);
if(p == NULL){
perror(NULL);
return 1;
}
strcpy(p,"ラーメン");
printf("旧:%s\n",p);
p = realloc(p,strlen("ラーメン")+strlen("+替え玉")+1);
if(p == NULL){
//旧領域を指すポインタは上塗りされたので、もう参照できずメモリリークする
perror(NULL);
return 1;
}
strcat(p,"+替え玉");
printf("新:%s\n",p);
free(p);
}
p = realloc(p,1024);のようにrealloc()の第一引数と戻り値の変数を同じにしてはいけません。
realloc()失敗時に古い領域を指すpがNULLに上書きされるので古い領域が解放不能になります。
■古い領域を参照するのは間違い
warning: Use of memory after it is freed
警告:解放後メモリ参照
[unix.Malloc]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
char *oldp = malloc(strlen("ラーメン")+1);
if(oldp == NULL){
perror(NULL);
return 1;
}
strcpy(oldp,"ラーメン");
printf("旧:%s\n",oldp);
char *newp = realloc(oldp,strlen("ラーメン")+strlen("+替え玉")+1);
if(newp != NULL){
strcpy(newp,oldp); //間違い:自力で新旧転送不要
free(oldp); //間違い:自力で解放不要
strcat(newp,"+替え玉");
printf("新:%s\n",newp);
free(newp);
}
}
./a.out
旧:ラーメン
free(): double free detected in tcache 2
Aborted
realloc成功時の以下2項目は
realloc関数がやるのであって
プログラマが自分で書いてはダメです。
(1)旧領域から新領域へのデータの引っ越し
(2)古い領域の解放
手元のLinux環境では
free(): double free detected in tcache 2
とエラーを吐いて異常終了しました。
参考: