我正在阅读悬空指针,发现这样做是一个很好的习惯,以防止自己悬空指针错误。
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);
free
函数导致ptr
指向的空间被解除分配,即 可用于进一步分配。如果 ptr 为空指针,则不会执行任何操作。否则,如果 参数与内存管理之前返回的指针不匹配 函数,或者如果空间已通过调用free
或realloc
来释放,则 行为未定义。
遵循两个强调点。
- 您可以传递空指针
NULL
,以根据需要多次free()
,它们是有效的调用(只是要忽略)。 - 不能传递已传递给
free()
一次的指针。
案例1:
在这里,在使用指针调用free()
后,我们将指针显式设置为NULL
。这就是为什么以后使用相同的指针变量调用free()
,任何时间,都不是问题。
案例2:
C 使用按值传递进行函数参数传递。这就是为什么,当包装在函数中时,
free(pp);
按预期工作,(它将所需的指针传递给free()
),但是
pp = NULL;
是函数的本地,该更改不会反映给调用方。因此,再次调用该函数会导致双重释放,就像现在一样,
- 指针已传递给
free()
- 分配的
NULL
不会反映给调用方,因此指针未设置为 NULL - 在下一次调用中,我们已经再次通过 D 指针
free()
。
如前所述,这会调用未定义的行为。
解决方案:您需要将指向指针的指针作为被调用函数的参数传递safefree()
,并且从被调用函数中,您可以将指针值设置为NULL
以使其反映在调用方中。类似的东西
void safefree(void ** ptrToPtr)
{
free(*ptrToPtr);
*ptrToPtr= NULL;
}
并像
safefree (&ptrToBeFreed);
将完成这项工作(无论如何,请注意那里的类型)。
除了乔纳森的评论之外,如果你做了一些导致未定义行为的事情(再次释放指针),它会导致未定义的行为(错误、崩溃)。
在不同的上下文中做同样的事情并不一定会导致相同的未定义行为,这取决于实现细节;它们通常只有编译器开发人员知道,所以它们从外部看起来是"随机的"。通过分析随机的未定义行为,什么都学不到。例如,编译时可能会影响结果...,或者您工作的目录的拼写...什么。