C 双重自由问题
如何通过编写一个名为free的包装函数来解决双重自由问题,这样我就不需要更改源代码中的每个自由调用了?不要这样做 不,真的。解决实际问题。一旦对指针调用了free(),您的代码就不应该以任何理由保留它。清空它,这样你就不能再释放它了;这将使陈旧指针取消引用引起的任何其他问题也可见。不要这样做 一种简单的方法是定义包装器函数,然后#定义free来调用函数C 双重自由问题,c,C,如何通过编写一个名为free的包装函数来解决双重自由问题,这样我就不需要更改源代码中的每个自由调用了?不要这样做 不,真的。解决实际问题。一旦对指针调用了free(),您的代码就不应该以任何理由保留它。清空它,这样你就不能再释放它了;这将使陈旧指针取消引用引起的任何其他问题也可见。不要这样做 一种简单的方法是定义包装器函数,然后#定义free来调用函数 #undef free #define free(x) wrapper_free(x) /* ... */ int *data = malloc
#undef free
#define free(x) wrapper_free(x)
/* ... */
int *data = malloc(42);
free(data); /* effectively calls wrapper_free(data) */
但是。。。不要那样做 我同意其他帖子说你不应该这样做。主要是,如果有人查看您的代码并试图弄清楚发生了什么(当他们习惯于只看到释放内存时),他们会更加困惑 如果您正在寻找一段允许您释放和清除指针的单行代码,我建议您使用宏,尽管还有其他方法(创建一个将指针指向指针的方法) 编辑 正如Christoph在一篇评论中提到的,您还可以确保用户将宏用作函数(在这样的情况下,使用do以分号结束行):
#define FREEANDCLEAR(pointer)\
do {\
free(pointer);\
pointer = 0;\
} while(0)
它将执行一次,并需要一个结束分号。查看stackoverflow问题的答案
使用这些答案中的工具或技术可以让您合并一个内存管理器,用于检查这些类型的问题,以便您能够识别和修复它们(正如您的问题的许多其他答案所表明的那样,使用类似这样的方法作为解决方法不是一个好主意)。以下代码截取对
malloc()的调用
、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
Sourcememdebug.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);
}
#包括
#包括
#包括
#ifndef MEMDEBUG_表格大小
//默认情况下记录16k分配
#定义MEMDEBUG_表大小0x4000
#恩迪夫
静态void*alloc_table[MEMDEBUG_table_SIZE];
静态大小\u t顶部;
void*memdebug_日志(void*ptr)
{
断言(top继续\n,
func、文件、行、ptr_arg);
返回;
}
对于(大小i=top;i--)
{
if(ptr==alloc_表[i])
{
免费(ptr);
--顶部;
如果(i!=top)alloc_table[i]=alloc_table[top];
返回;
}
}
fprintf(标准,
%s()在%s的第%i行:释放无效指针“%s”-->退出\n,
func、文件、行、ptr_arg);
退出(退出失败);
}
除了所有其他关于内存管理开发等的答案和参考资料之外
当双自由发生时,代码中会出现错误,表明事情没有按正确的顺序进行,等等
大约10年前,当我开始使用Java时,每个人都称赞Java,因为您不必为new/delete操心
我很烦恼(因为我习惯于在C/C++中开发),因为突然之间,除了malloc/free之外,在软件中以许多方式出现的所有打开/关闭、创建/销毁、锁定/解锁模式都可能被打破,因为每个人都认为所有清理都是自动的
因此,当您遇到这些问题时,您可能迟早也会遇到其他问题。资源被占用,数据库连接被消耗,文件系统上的文件被锁定
Double free表示您的程序没有良好的结构,例如在一个层中分配的东西在另一个层中被释放。+1用于将指针设置为NULL。这实际上是不使用Double free的最简单解决方案。在任何情况下,编写一个自定义的
my_free()
来执行free()
然后将指针设置为NULL似乎是确保这一点的最简单方法。但这涉及到代码更改:-)包装器函数处理指针为NULL的优点。有些语言甚至包括这些,使用它们总是一种很好的做法。Delphi对象有一个free()方法,但最好使用FreeAndNil()全局函数。当然,当指针已经为null时,它也不会抛出错误,而free会抛出错误。@Chris:是什么让你认为free(null)
会抛出错误?ANSI标准,第4.10.3.2节:“如果ptr是空指针,则不会发生任何操作。”“修复实际问题”-如果您不帮助他编写一个free
替换,突出显示双自由发生的位置,您认为他将如何解决问题?如果您有同一指针的两个副本,则通过free()将其中一个设置为空或者免费包装()不会阻止释放第二个副本的尝试。如果(指针)-免费(空)
有效且不执行任何操作,则无需检查;另外,您可能希望将其包装在一个do..while()
循环中,以获得类似syntaxThank Christoph的函数,我删除了if,并使用do while添加了第二个实现!在进行辩护之前,您应该先执行一个#undef free
:C标准允许stdlib函数按照以下方式进行传统实现:macros@pmg你能解释一下原因吗?@gewure:free()
有一个非常精确的定义。从长远来看,用其他东西取代它肯定会伤害你的脚(你会比你想象的更早忘记这个替代品)。如果在头文件中给出“重新定义”,则尤其如此。@pmg-well。。。尽管如此,我还是这样做了(不是免费的stdlib)
#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);
}