不要那样做。
真的,修复实际问题。一旦在指针上调用了free()函数,你的代码就不应该出于任何原因继续持有它。将其置为null,这样你就无法再次释放它;这将使由于过期的指针解引用而引起的任何其他问题变得明显可见。
my_free()
函数中可以先调用free()
释放内存,然后将指针设为NULL。但需要对代码进行修改。 - Joeyfree(NULL)
会抛出一个错误,是什么让你这样想? 根据ANSI标准第4.10.3.2节:“如果ptr是空指针,则不执行任何操作。” - Christophfree
替代品,你认为他会如何解决这个问题? - Steve Jessopfree
替代方案以突出双重释放的位置”不在问题的范围内 - 只需要一个不会引起问题的替代方案。通过精确跟踪指针的分配和释放位置和/或制定一些编程规则/启发式来确定代码的哪些部分负责每个操作,可以实现“修复实际问题”。 - Jason Musgrove不要那样做
一个简单的方法是定义您的包装函数,并使用 #define 将 free 调用您的函数。
#undef free
#define free(x) wrapper_free(x)
/* ... */
int *data = malloc(42);
free(data); /* effectively calls wrapper_free(data) */
但是...不要那样做!
#undef free
:C 标准允许 stdlib 函数被额外实现为宏。 - Christophfree()
有非常精确的定义。用其他东西替换它长期来看肯定会让你后悔(你会比想象中更快地忘记替换)。特别是如果你在头文件中进行“重新定义”。 - pmgmalloc()
, realloc()
和 calloc()
的调用以进行日志记录。free()
时,它会检查内存是否先前是由这些函数之一分配的。如果不是,则程序将被终止。释放空指针将被报告,但执行将继续。memdebug.h
:#undef free
#define free(PTR) memdebug_free(PTR, #PTR, __FILE__, __func__, __LINE__)
#undef malloc
#define malloc(SIZE) memdebug_log(malloc(SIZE))
#undef realloc
#define realloc(PTR, SIZE) memdebug_log(realloc(PTR, SIZE))
#undef calloc
#define calloc(COUNT, SIZE) memdebug_log(calloc(COUNT, SIZE))
#ifndef MEMDEBUG_H
#define MEMDEBUG_H
extern void memdebug_free(void *ptr, const char *ptr_arg, const char *file,
const char *func, int line);
extern void *memdebug_log(void *ptr);
#endif
来源 memdebug.c
:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef MEMDEBUG_TABLE_SIZE
// log 16k allocations by default
#define MEMDEBUG_TABLE_SIZE 0x4000
#endif
static void *alloc_table[MEMDEBUG_TABLE_SIZE];
static size_t top;
void *memdebug_log(void *ptr)
{
assert(top < sizeof alloc_table / sizeof *alloc_table);
alloc_table[top++] = ptr;
return ptr;
}
void memdebug_free(void *ptr, const char *ptr_arg, const char *file,
const char *func, int line)
{
if(!ptr)
{
fprintf(stderr,
"%s() in %s, line %i: freeing null pointer `%s` -->continue\n",
func, file, line, ptr_arg);
return;
}
for(size_t i = top; i--; )
{
if(ptr == alloc_table[i])
{
free(ptr);
--top;
if(i != top) alloc_table[i] = alloc_table[top];
return;
}
}
fprintf(stderr,
"%s() in %s, line %i: freeing invalid pointer `%s`-->exit\n",
func, file, line, ptr_arg);
exit(EXIT_FAILURE);
}
我同意其他帖子的观点,认为你不应该这样做。主要是因为当别人习惯于只看到free函数释放内存时,他们会更加困惑,试图弄清楚代码中发生了什么。
如果你正在寻找一行代码来释放指针,我建议使用宏,虽然还有其他方法可以实现这一点(例如创建一个接受指向指针的指针的方法)。
#define FREEANDCLEAR(pointer)\
{\
free(pointer);\
pointer = 0;\
}
编辑 正如Christoph在评论中提到的那样,您还可以通过使用do while来确保用户像使用函数一样使用宏(在行末使用分号):
#define FREEANDCLEAR(pointer)\
do {\
free(pointer);\
pointer = 0;\
} while(0)
这将执行一次并需要一个结束分号。
if(pointer)
- free(NULL)
是有效的且什么也不做;此外,您可能希望将其包装在 do..while()
循环中以获得类似函数的语法。 - Christoph除了其他答案和与内存管理开发等相关的参考之外,当双重释放发生时,代码中会出现错误,表明事情没有按正确顺序完成等。
10年前我开始使用Java工作时,每个人都赞扬Java,因为你不必担心new/delete。
我感到困扰(因为我习惯于在C/C++中开发),因为突然间所有打开/关闭、创建/销毁、锁定/解锁模式,在malloc/free之外以许多方式显示在软件中,这些模式可能会被破坏,因为每个人都认为所有清理都是自动的。
因此,当您遇到这些问题时,您很可能会有其他问题,早晚会出现资源占用、数据库连接被消耗、文件在文件系统上被锁定等问题。
双重释放是您的程序结构不好的信号,例如在一层中分配的东西在另一层中被释放。
查看stackoverflow问题"编写自己的内存管理器"的答案。
使用这些答案中的工具或技术将让您集成一个内存管理器,该管理器检查此类问题,以便您可以识别并修复它们(正如您问题的许多其他答案所指出的那样,使用此类方法作为解决方法不是一个好主意)。