问题描述
我有一个用Ubuntu编写的,用GCC编译的新C程序。 (gcc版本7.4.0(Ubuntu 7.4.0-1ubuntu1〜18.04.1))我的代码malloc()
和free()
的结构非常多。我的程序运行了几个小时后,它崩溃并显示以下消息:
double free or corruption (fasttop)
Aborted (core dumped)
我假设“双重释放”意味着我的代码在先前free()
结构上调用malloc()
至少两次。那很有可能。根据我的输入,我的代码创建了许多不同的结构,尽管我有最好的意图,但它可能试图两次free()
。
因此,我正在与valgrind一起查找问题,但是我想到了另一种解决方案。为什么我的代码不能查看指向该结构的指针并知道该结构是否已经free()
?我试图设计一个解决方案。考虑:
typedef struct MeStruct{
int a,b,c;
}me;
me* buildMe( int a,int b,int c ){
me* ret = malloc(sizeof(me));
ret->a = a; ret->b = b; ret->c = c;
return ret;
}
void printMe( me* me1 ){
printf("------------------------------\n");
printf("printMe() :: pointer address is: %p\n",me1);
if( me1 == NULL ){
printf("NULL\n");
} else {
printf( "ME struct :: %d %d %d\n",me1->a,me1->b,me1->c );
}
printf("------------------------------\n");
}
void freeMe( me* me1 ){
printf("Calling freeStuff()\n");
if( me1 != NULL ){
free( me1 );
me1 = NULL; // DOES THIS DO ANYTHING???
}
}
int main( int argc,char **argv ){
me* me1 = buildMe( 1,2,3 );
printMe( me1 );
freeMe( me1 ); // First attempt to free( me1 )
printMe( me1 );
freeMe( me1 ); // Second attempt to free( me1 )
printf("END OF PROGRAM.\n");
return 1;
}
所以起初,我认为这很聪明。当free()
结构被使用时,代码会将结构的指针设置为NULL,从而提供了一种方法来检测该结构是否有效。但是标有“ DOES THIS DO ANYTHIING???
”的行似乎无济于事。输出如下:
------------------------------
printMe() :: pointer address is: 0x555a71097260
ME struct :: 1 2 3
------------------------------
Calling freeStuff()
------------------------------
printMe() :: pointer address is: 0x555a71097260
ME struct :: 0 0 3
------------------------------
Calling freeStuff()
END OF PROGRAM.
因此,这里对printMe()
的第二次调用很有趣。此时,该结构已被free()
编辑,这意味着不再分配分配给该结构的堆内存。但是,结构的指针没有改变,也许我什至可以看到一些原始数据? (me1->c
仍为值“ 3”)
因此,看来我的方案无效,至少在此编译器上无效。几个问题困扰着我。首先, 为什么 不起作用?为什么在调用freeMe()
之后指针的值不为NULL?又为什么第二次调用freeMe()
导致程序崩溃?毕竟,第二次调用尝试第二次free()
结构。
感谢任何想法或批评,谢谢。
编辑:我修复了复制/粘贴错误,该错误为“返回1;”。被放在“ printf(“程序结束。\ n”);“之前;”致电main()
。
解决方法
指针只是一个数字/内存地址。 free()
一旦使用它,它就会保留其值,并且仍然是一个指针,尽管它不再指向有效数据。它指向的内存现在可以由内存分配器用于后续对malloc()
的调用。
释放该内存后,您可能仍会从结构中看到过时的值,因为free()
没有义务将释放的内存清零。但是您不应再访问它。如果您随后调用malloc()
并用更多数据填充堆,则很可能会看到数据被覆盖。
正如Nate Eldredge解释的那样,C函数按值传递参数。指针未被修改的原因与以下示例中的v
在调用foo()
之后保持不变一样:
void foo(int a) {
a = 3; // only modifies a in this local context
}
void bar(int *p) {
*p = 3; // modifies what p is pointing to,i.e. v
}
int main(void) {
int v = 1;
foo(v); // v is copied to foo
assert(v == 1); // v remains untouched
bar(&v); // pointer to v is passed to bar
assert(v == 3); // which is successfully modified
return 0;
}
您传递给freeMe()
的指针值与本示例中的整数值一样,不同之处在于它恰好指向某个内存地址。
请注意,双重释放会引发未定义的行为,因此,free()
可以在第二次调用相同地址时做任何想要的事情---不能必须报告两次免费错误。
如果要为指针分配一个永久的NULL
值,则需要传递指针的内存地址,而不是指针。
void freeMe1(char *temp) {
free(temp); // will free memory; its effect is persistent
temp = NULL; // will assign NULL to pointer but its effect is local only
}
void freeMe2(char **temp) {
free(*temp); // will free memory; its effect is persistent
*temp = NULL; // will assign NULL to pointer and its effect is persistent
}
...
int main {
char *str = malloc(...);
...
...
freeMe1(str); // str is not nullified
freeMe2(&str); // str is nullified
...
}