【问题标题】:Fail to understand why freeing memory throws an error无法理解为什么释放内存会引发错误
【发布时间】:2017-03-15 04:56:40
【问题描述】:

我正在阅读 Dangling Pointer 并发现这样做是一个好习惯,以防止自己出现悬空指针错误。

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

现在我决定用一个示例 vanilla 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 中没有失败并且在示例代码的后半部分失败。

【问题讨论】:

  • 可以更改safefree()中指针的副本;您不能在具有该签名的调用代码中更改它。你需要:void safefree(void **vpp) { free(*vpp); *vpp = NULL; }——你会打电话给safefree(&ptr); safefree(&ptr);。你必须确保你有一个void *。我想你可以坚持使用char *,如果你使用:void safefree(char **cpp) { free(*cpp); *cpp = NULL; }(和safefree(&ptr);仍然),但这只能用于释放char *而不是double *等。
  • @JonathanLeffler 你能解释一下为什么我必须在方法/函数中将指针传递给ptr(指针)。
  • 因为你不能修改调用函数中的指针,除非你得到一个指向指针的指针。就像您不能在调用函数中更改 int 变量一样,除非向被调用函数传递了 int *,否则您无法在调用函数中更改 char *,除非向被调用函数传递了 char ** .
  • 类似函数的宏在这里可能很有用...它可以修改您传递给它的指针,而不需要额外的间接级别。但是,如果您能很好地跟踪您的分配情况,则通常不需要它。
  • #define safefree(X) do { free(X); (X)=NULL; } while(0) 这样的东西会让你只使用safefree(ptr);。由于它是文本替换而不是函数调用,因此宏扩展 do {free(ptr); (ptr)=NULL; } while(0); 是您使用它的函数的一部分,它修改了原始 ptr 而不是它的副本。但是,您应该始终知道何时需要释放指针,而不是依赖这样的东西来避免关注:)

标签: c pointers malloc free


【解决方案1】:

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

void free(void *ptr);

free函数使ptr指向的空间被释放,也就是使 可用于进一步分配。 如果 ptr 是空指针,则不执行任何操作。否则,如果 该参数与内存管理先前返回的指针不匹配 函数,或者如果空间已通过调用freerealloc 释放,则 行为未定义。

遵循强调的两个点。

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

案例一:

在这里,在使用指针调用free() 之后,我们明确 将指针设置为NULL。这就是为什么以后用相同的指针变量调用free(),任何时间都不是问题。

案例2:

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

free(pp);

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

pp = NULL;

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

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

如前所述,这会调用undefined behavior

解决方法:你需要传递一个指向指针的指针作为被调用函数safefree()的参数,并且从被调用函数中,你可以将指针值设置为NULL来获取它反映在来电者身上。类似的东西

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

和打电话一样

 safefree (&ptrToBeFreed);

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

【讨论】:

  • 非常感谢您的解释。
【解决方案2】:

除了乔纳森的话,如果你做了一些导致未定义的行为(再次释放指针),它会导致未定义的行为(错误、崩溃)。

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

【讨论】:

    猜你喜欢
    • 2020-03-14
    • 2017-03-20
    • 2014-05-18
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-19
    • 1970-01-01
    相关资源
    最近更新 更多