设置条件以避免双重释放分配的内存

Posted

技术标签:

【中文标题】设置条件以避免双重释放分配的内存【英文标题】:Setting a condition to avoid double freeing allocated memory 【发布时间】:2011-05-14 05:55:56 【问题描述】:

Ubuntu 10.10 gcc 4.4.4

我只是在尝试分配和释放。

但是,当一个对象被多次释放时,我试图避免这个问题。

但是,当我测试时,我注意到创建和释放的 obj 不会返回到 null 状态。那么如果发生这种情况,我可以设置什么条件来避免这种情况?

我还尝试在释放后将对象设置为 NULL。但是,它仍然试图释放该对象。

这是对这个问题的引用,只是为了让您知道不是重复的: freeing allocated memory

我的代码如下:

#include <stdio.h>
#include "objects.h"

int main(void)

    obj_t *obj = NULL;

    obj = create_object();

    destroy_object(obj);

    destroy_object(obj);

    return 0;

==

#ifndef OBJECTS_H_INCLUDED
#define OBJECTS_H_INCLUDED

typedef struct Obj_t obj_t;

obj_t* create_object();
void destroy_object(obj_t *obj);

#endif /* OBJECTS_H_INCLUDED */

==

#include <stdio.h>
#include <stdlib.h>

#include "objects.h"

struct Obj_t 
    int obj_id;
;

obj_t* create_object()

    obj_t *obj = malloc(sizeof obj);

    return obj;


void destroy_object(obj_t *obj)

    if(obj != NULL) 
        printf("Object [ %d ] will be deleted\n", obj->obj_id);
        free(obj);
    

==

OBJECT_FILES = objects.o main.o
CFLAGS = -Wall -Wextra -Wunreachable-code -ggdb -O0
CC = gcc
TARGET = obj

$(TARGET): $(OBJECT_FILES)
    $(CC) $(CFLAGS) $(OBJECT_FILES) -o $(TARGET)

main.o: main.c objects.c
    $(CC) $(CFLAGS) -c main.c

objects.o: objects.c
    $(CC) $(CFLAGS) -c objects.c 

clean:
    rm -f $(OBJECT_FILES) $(TARGET) *~

【问题讨论】:

如果您将指针设置为NULL 并再次尝试free 它是空操作。你在哪里将指针设置回NULL 请注意,在 C(但不是 C++)中,obj_t *create_object(); 的标头中的声明表示 create_object() 是一个函数,其参数列表未定义 - 非空 - 返回一个 obj_t指针。编译器无法确保您不会误称它,因为您没有告诉它参数列表应该是什么。你应该在声明中写obj_t *create_object(void);,然后为了对称,在函数定义中写obj_t *create_object(void) ... @Jon,感谢您提供有关 void 参数的提示。 【参考方案1】:

这不是一个全新的想法,因此您可能想了解Reference Counting 和Smart Pointers。

Standard Template Library (STL) 和 Boost 都大量使用了这些概念。

【讨论】:

【参考方案2】:

Objective-C 执行reference counting,一旦对象的引用计数达到零,就释放对象。您也可以查看smart pointers。

【讨论】:

是的,Objective-C 是 C 的超集。 但是您可以轻松实现自己的引用计数版本。【参考方案3】:

您不能在destroy_object 函数中将指针设置为NULL,因为函数中指针的值是指针的副本

在 C 和 C++ 中,程序员如何避免两次释放或删除一个对象是不这样做

如果您发现自己编写的程序无法确定对象有多少引用,那么首先要做的是重新考虑您的设计。

如果你真的必须,使用 C++ shared_ptr,或者在 C 中实现你自己的引用计数方案。Perl 和 Python 在它们的 C 实现中都是这样做的。

【讨论】:

@ant2009 您可以使用引用调用,这样您将处理实际的 obj 而不是副本。见:tutorialspoint.com/cprogramming/…【参考方案4】:

问题在于Main 中的指针值永远不会更新,因为您将指针的副本传递给destroy_object。你需要的是一个指向指针的指针。试试这个:

void destroy_object(obj_t **obj)

    if(*obj != NULL) 
        printf("Object [ %d ] will be deleted\n", *obj->obj_id);
        free(*obj);
        *obj = NULL;
    

然后:

destroy_object(&obj);

【讨论】:

【参考方案5】:

通常,一个对象只有一个有权删除它的所有者是一种很好的做法。同时,您可以保留一个指向该对象的指针,并确保在删除对象后将该指针设置为 NULL。或者,总是有标准的 C++ 解决方案:智能指针。

【讨论】:

【参考方案6】:

正如其他人所指出的,在destroy_object() 内将obj 设置为NULL 正在更改局部变量,并且在main() 中无效。

但是,如果您希望 destroy_object() 尝试警告程序员有关双重释放,作为调试帮助,那么您可以使用 poison 值的概念。在您的示例中,您可能会保留一些特定的无效obj_id 值来表示“已释放” - 例如-1。您的 destroy_object() 函数将如下所示:

#define OBJ_POISON (-1)

void destroy_object(obj_t *obj)

    if (obj != NULL) 
        if (obj->obj_id == OBJ_POISON) 
            fprintf(stderr, "Double-free of object @ %p detected, aborting.\n", obj);
            abort();
        

        printf("Object [ %d @ %p ] will be deleted\n", obj->obj_id, obj);
        obj->obj_id = OBJ_POISON; 
        free(obj);
    

(当然,要使 create_object() 函数正常工作,还必须将 obj_id 设置为有效值,例如 0)。

请注意,这并不是为了让双重释放“正常工作”——它只是为了让它在调试时更加可见。

【讨论】:

您可以使用static const obj_t poison;#define OBJ_POISON (&amp;poison) 或类似的代替实现定义的-1 转换为指针来获取毒药。那么指针就保证是唯一的。 它实际上是分配结构中的一个普通int,我设置为-1。显然,读取释放的内存仍然是一个错误 - 但有可能毒值将保留在那里。

以上是关于设置条件以避免双重释放分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

C 语言二级指针案例 ( 多级指针内存释放问题 | 多级指针避免野指针 )

从向量中擦除对象会导致双重释放[重复]

如何正确释放 ID3D11ShaderResourceView** 以避免内存泄漏?

当原始指针和唯一指针都引用同一个对象时,如何避免双重释放?

将numpy数组设置为无释放内存吗?

【golang】内存逃逸常见情况和避免方式