无法理解为什么释放内存会引发错误

Fail to understand why freeing memory throws an error

我在阅读悬挂指针,发现这样做是个好习惯,可以防止自己出现悬挂指针错误。

free(ptr); // free the ptr
ptr = NULL; 

现在我决定使用示例香草 C 代码对此进行测试。

CASE_1

char *ptr = malloc(10);
... 
...
free(ptr); 
ptr=NULL;
// Just to check what happen if I call free more than I once
free(ptr)
ptr=NULL;

一切正常。在我决定将 free 和指针 NULL 赋值包装在 function 之前,我将其命名为 safefree

void safefree(char *pp) {
  free(pp);
  pp = NULL;
}

CASE_2

现在,当我运行上面的方法不止1个(像这样)

safefree(ptr);

safefree(ptr);

我收到以下错误。

malloc: *** error for object 0x7fd98f402910: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

我碰巧理解了错误(未分配正在释放的指针) 但我不明白为什么它在 CASE_1 中没有失败,而在示例代码的后半部分却失败了。

首先,让我们回顾一下 free() 的行为。引用 C11,章节 §7.22.3.3

void free(void *ptr);

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

按照强调的两个点。

  • 您可以将空指针 NULL 传递给 free() 任意多次,它们是有效的调用(只是被忽略)。
  • 您不能传递已经传递给 free() 一次的指针。

案例 1:

这里,在用指针调用 free() 之后,我们 显式 将指针设置为 NULL。这就是为什么以后使用相同的指针变量调用 free() 任意次数都不是问题的原因。

案例 2:

C 在函数参数传递中使用按值传递。这就是为什么,当包裹在一个函数中时,

free(pp);

按预期工作,(它将所需的指针传递给 free())但是

pp = NULL;

是函数本地的,并且该更改不会反映到调用方。因此,再次调用该函数会导致双重释放,就像现在一样,

  • 指针已经传递给free()
  • 分配的 NULL 未反映给调用者,因此指针未设置为 NULL
  • 在下一次调用中,我们将再次传递 already-free()d 指针。

如前所述,这会调用 undefined behavior.

解决方法:你需要传递一个指向指针的指针作为被调用函数的参数safefree()并且从被调用函数中,你可以设置指针值到 NULL 以将其反映在调用者中。像

void safefree(void ** ptrToPtr) 
{ 
     free(*ptrToPtr); 
     *ptrToPtr= NULL; 
 }

并且喜欢打电话

 safefree (&ptrToBeFreed);

会完成这项工作(无论如何要注意那里的类型)。

除了 Jonathan 的评论之外,如果您执行的操作会导致未定义的行为(再次释放指针),它会导致未定义的行为(错误、崩溃)。

在不同的上下文中做同样的事情并不一定会导致相同的未定义的行为,这取决于实现细节;它们通常只为编译器开发人员所知,因此从外部看来它们 'random'。通过分析随机的未定义行为无法学到太多东西。例如,可能是编译时间影响了结果……,或者您工作的目录的拼写……任何东西。