【问题标题】:Is there a way to set all the pointers pointing to the freed memory to NULL?有没有办法将所有指向已释放内存的指针设置为 NULL?
【发布时间】:2020-03-22 05:54:50
【问题描述】:

我想将所有指向已释放内存位置的指针设置为 NULL,这样就不会出现悬空指针或双重释放。这在 C 中可能吗?

例如,我有以下结构:

struct B {
    int *arr;
    unsigned int len;
};

struct A {
    struct B *b;
};

// Freeing and setting them to NULL:
bool test_safe_free() {
    struct A *a = malloc(sizeof(struct A));
    struct B *b = malloc(sizeof(struct B));
    b->arr = malloc(100 * sizeof(int));
    b->len = 100;
    a->b = b;

    safe_free_A(&a);
    return a == NULL && b == NULL;
}

void safe_free_B(struct B **b_ref) {
    if (*b_ref != NULL) free((*b_ref)->arr);
    (*b_ref)->arr = NULL;
    free(*b_ref);
    *b_ref = NULL;
}

void safe_free_A(struct A **a_ref) {
    // Before freeing A, freeing B:
    if (*a_ref != NULL) safe_free_B(&((*a_ref)->b));
    free(*a_ref);
    *a_ref = NULL;
}

test_safe_free函数返回false,因为即使变量a在释放后设置为NULL,b仍然指向释放的内存,因为指针被复制了(并且副本设置为NULL,而原来的保持不变)当a传入函数时。

我想不出一种方法,一个结构来解决这个问题,但我也不确定我正在尝试做的事情是否可能。

【问题讨论】:

  • @JonathonReinhart 感谢您提供信息。
  • 即使你能做到这一点,这也是个坏主意。那些神奇地设置为 NULL 的指针,如果使用的话,会导致混乱。您的问题是垃圾收集的绝佳论据。
  • @ddyer 什么乱七八糟的?该标准实际上允许编译器将任何指向已释放存储的指针设置为 NULL 或任何其他值。如果程序尝试使用指针的值来释放存储空间,则程序具有未定义的行为
  • @ddyer 没有那样想。当我释放一个包含另一个结构 B 的结构 A 时,我想避免双重释放,然后在其他地方释放该结构 B。例如:如果我有一个结构 A 的列表和另一个 B 的列表,如果该列表中的一些 B 已经在 A 列表中的一些 A 中,并且我尝试释放这两个列表,我将获得双重释放。我能想到的避免跟踪在哪里释放什么并避免双重释放的唯一方法是将指向已释放内存位置的每个指针设置为 NULL。

标签: c pointers memory-management dynamic-memory-allocation


【解决方案1】:

C 语言不会跟踪您对指针变量所做的所有副本。

C++ 可以通过智能指针的概念做到这一点,但是 C 不能,因为它没有类似于析构函数的功能。由程序员负责跟踪任何引用并适当地管理它们。

实现此目的的一种方法是实现引用计数,这并非易事。您需要创建一个结构,该结构将充当所有已分配内存段的标头,这些内存段包含当前引用计数。然后,您需要为malloc 和家族创建一个包装器,为标题加上请求的空间分配空间,在内存块的开头写入标题,然后在块之后返回指向内存的指针。然后,您需要一个可以增加您在创建新引用时手动调用的引用计数的函数。然后你需要一个函数来减少引用计数,当它达到 0 时会调用 free

当然,即使你这样做了,你也要知道何时不调用free,而记住在必要时增加/减少引用计数。递增失败意味着使用释放的内存和/或双重释放,而递减失败意味着内存泄漏。

【讨论】:

    【解决方案2】:

    在这个函数中

    bool test_safe_free() {
        struct A *a = (struct A *) malloc(sizeof(struct A));
        struct B *b = (struct B *) malloc(sizeof(struct B));
        b->arr = (int *) malloc(100 * sizeof(int));
        b->len = 100;
        a->b = b;
    
        safe_free_A(&a);
        return a == NULL && b == NULL;
    }
    

    您声明了未传递给safe_free_B 的指针b。通过引用传递给函数safe_free_B的是指针a->b

    safe_free_B(&((*a_ref)->b));
    

    所以函数test_safe_free中声明的局部变量b保持不变。

    你可以写在函数里

    a->b = b;
    b = NULL;
    
    safe_free_A(&a);
    return a == NULL;
    

    现在分配内存的所有者是指针a->b,而不是指针b

    尽管如此,safe_free_Bsafe_free_A 函数并不安全。:)

    你可以这样写

    void safe_free_B(struct B **b_ref) {
        if ( *b_ref != NULL ) free((*b_ref)->arr;
        free(*b_ref);
        *b_ref = NULL;
    }
    

    void safe_free_A(struct A **a_ref) {
        // Before freeing A, freeing B:
        if ( *a_ref != NULL ) safe_free_B(&((*a_ref)->b));
        free(*a_ref);
        *a_ref = NULL;
    }
    

    【讨论】:

    • 啊,是的,当然,通常“安全”意味着零安全。我想我应该编辑它。谢谢。关于将b设置为NULL,我不确定我是否可以在我的源代码中实现它,但一定会尝试,再次感谢!
    猜你喜欢
    • 2013-07-30
    • 2015-05-01
    • 2016-03-01
    • 2012-12-28
    • 2021-07-23
    • 2022-10-14
    • 2010-12-25
    • 2018-07-02
    • 1970-01-01
    相关资源
    最近更新 更多