有没有一种方法可以将所有指向已释放内存的指针设置为NULL?

时间:2019-11-26 18:25:48

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

我想将所有指向释放内存位置的指针设置为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仍指向释放的内存,因为指针被复制(将a传递给函数时,将copy设置为NULL,而原始副本保持不变。

我无法提出一种解决该问题的方法,但是我也不确定我尝试做的事情是否可能。

2 个答案:

答案 0 :(得分:5)

C语言无法跟踪您对指针变量所做的所有复制。

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

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

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

答案 1 :(得分:1)

此功能

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;
}

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

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

因此,在函数b中声明的局部变量test_safe_free保持不变。

您可以在函数中编写

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;
}