C 双重自由问题

C 双重自由问题,c,C,如何通过编写一个名为free的包装函数来解决双重自由问题,这样我就不需要更改源代码中的每个自由调用了?不要这样做 不,真的。解决实际问题。一旦对指针调用了free(),您的代码就不应该以任何理由保留它。清空它,这样你就不能再释放它了;这将使陈旧指针取消引用引起的任何其他问题也可见。不要这样做 一种简单的方法是定义包装器函数,然后#定义free来调用函数 #undef free #define free(x) wrapper_free(x) /* ... */ int *data = malloc

如何通过编写一个名为free的包装函数来解决双重自由问题,这样我就不需要更改源代码中的每个自由调用了?

不要这样做

不,真的。解决实际问题。一旦对指针调用了free(),您的代码就不应该以任何理由保留它。清空它,这样你就不能再释放它了;这将使陈旧指针取消引用引起的任何其他问题也可见。

不要这样做

一种简单的方法是定义包装器函数,然后#定义free来调用函数

#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
Source
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);
}
#包括
#包括
#包括
#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);
}