前提
1つ前の質問の続きです。
↑この質問において、プログラムの記述が間違っている指摘を受けたため、
自分なりに調べて修正しようとしたのですが、どうやってもうまくいきませんでした。
(free()を1回しか実行していないのに、なぜか二重解放したことになって怒られるfree(): double free detected in tcache 2
)
1日かけていろいろ実験したところ、問題を再現する簡単なプログラムAを作成出来たため、それについて質問させていただきます。
発生している問題
c
1#include <stdio.h> 2#include <stdlib.h> 3 4typedef struct { 5 int num;//数字を保存 6 char c;//アルファベットを保存 7} Kouzo; 8 9void func(Kouzo *ptr); 10 11int main(void){ 12 Kouzo *ptr = malloc(sizeof(Kouzo)); //要素1つ分の構造体を作成 13 14 ptr[0].num = 10; 15 ptr[0].c = 'A'; 16 17 printf("addr in main bef:%p\n",ptr); 18 19 func(ptr); 20 21 printf("addr in main aft:%p\n",ptr); 22 23 free(ptr); 24} 25 26void func(Kouzo *ptr){ 27 int loop = 1; 28 while(loop<10){ 29 ptr = realloc(ptr, (loop+1) * sizeof(Kouzo)); 30 ptr[loop].num = 10; 31 ptr[loop].c = 'A'; 32 loop++; 33 printf("addr in func:%p, loop:%d\n",ptr,loop); 34 } 35 36}
text
1//出力結果 2addr in main bef:0x2079260 3addr in func:0x2079260, loop:2 4addr in func:0x2079260, loop:3 5addr in func:0x2079690, loop:4 6addr in func:0x2079690, loop:5 7addr in func:0x2079690, loop:6 8addr in func:0x2079690, loop:7 9addr in func:0x2079690, loop:8 10addr in func:0x2079690, loop:9 11addr in func:0x2079690, loop:10 12addr in main aft:0x2079260 13free(): double free detected in tcache 2 14中止 (コアダンプ)
この出力より、なぜ二重解放のエラーが出たのかは理解出来ました。
a=realloc(b,size)
に成功すると、bが指す領域は解放(free()
)されて、aが新しく確保された領域を指すようになる。- この出力によると、loop4でアドレス0x2079260が解放されて、新しく0x2079690を確保している。
- func関数から戻ってきたとき、ポインタ
ptr
が指しているアドレスは0x2079260である。 - ここでfree(ptr)をすると、0x2079260を再度解放することになり、 二重解放となってエラーが発生する。
しかし、 「func(ptr)の実行前と実行後でポインタptrが指すアドレスが変わらない」という点が理解できません。
↓このようなプログラムBを考えます
c
1#include <stdio.h> 2 3void funcval(int a); 4void funcptr(int *a); 5 6int main(void){ 7 int a = 0; 8 9 printf("in main, a = %d\n",a); 10 funcval(a); 11 printf("after funcval, a = %d\n",a); 12 13 funcptr(&a); 14 printf("after funcptr, a = %d\n",a); 15 return 0; 16} 17 18void funcval(int a){ 19 a = 10; 20 printf("in funcval, a = %d\n",a); 21} 22 23void funcptr(int *a){ 24 *a = 100; 25 printf("in funcptr, a = %d\n",*a); 26} 27
text
1//出力結果 2in main, a = 0 3in funcval, a = 10 4after funcval, a = 0 5in funcptr, a = 100 6after funcptr, a = 100
このプログラムBのfuncvalは値渡しですから、funcval内でa
を変更しても、呼び出し元のa
は変更されません。
それに対して、funcptrはポインタ渡しなので、funcptrでa
を変更すると、呼び出し元のa
も変更されます。
以上より、関数の引数にポインタを置き、その関数でポインタを変更すると、呼び出し元は必ず変化するはずですが、プログラムAではそれが発生していません。
これは何故なのでしょうか。
補足情報
Red Hat 8.3.1
gcc 8.3.1
回答3件
あなたの回答
tips
プレビュー