如何释放在双指针结构上分配的内存

时间:2021-05-18 06:20:27

标签: c struct allocation

如何释放在这个结构中分配的内存

struct image_t {
    char type[3];
    int **ptr;
    int width;
    int height;
};

在第一个函数中我做了这些分配:

    struct image_t *struktura = (struct image_t *)malloc(sizeof(struct image_t));
    int **ptr = (int**)malloc(struktura->height * sizeof(int*));

    for (i = 0; i < struktura->height; i++) {
        *(ptr + i) = (int *)malloc(struktura->width * sizeof(int));
        if (*(ptr + i) == NULL) {
            break;
        }
    }

在第二个函数中,我必须释放分配的内存,所以我尝试释放类似这样的内存,但它不起作用

void destroy_image(struct image_t **m) {
    if (m != NULL) {
        if (*m != NULL) {
            if ((*m)->ptr != NULL) {
                for (int i = 0; i < (*m)->height; i++) {
                    free(*((*m)->ptr + i));
                }
                free((*m)->ptr);
                free(*m);
            }
        }
    }
}

我无法更改 destroy 函数的声明,因此结构上必须有双指针。

3 个答案:

答案 0 :(得分:2)

从...开始

  1. 不要转换 malloc 返回的值

  2. 不要使用 *(ptr + i) 使用等效但更具可读性的版本,即 ptr[i]

这样做会将您的分配更改为:

struct image_t *struktura = malloc(sizeof(struct image_t));
int **ptr = malloc(struktura->height * sizeof(int*));

for (i = 0; i < struktura->height; i++) {
    ptr[i] = malloc(struktura->width * sizeof(int));
    if (ptr[i] == NULL) {
        break;
    }
}

这是第一个问题……您永远不会将 ptr 分配给任何东西。在代码块的末尾需要添加:

struktura->ptr = ptr;

另一个问题是struktura->heightstruktura->width在使用时都没有初始化。必须在使用前为其赋值。

为了释放分配的内存:您当前的代码太复杂了。像 free(*((*m)->ptr + i)); 这样的语句包含 3 个指针取消引用!!。这很难读。我建议您使用一些局部变量来简化代码。

void destroy_image(struct image_t **m) {
    if (m == NULL) return;
    if (*m == NULL) return;
    struct image_t *t = *m;
    int **ptr = t->ptr;
    if (ptr != NULL)
    {
        for (int i = 0; i < t->height; i++) 
        {
            free(ptr[i]);
        }
        free(ptr);
    }
    free(t);
    *m = NULL;  // The only reason to pass a double pointer to this
                // function is to be able to change *m. I guess
                // the prototype authoe wants the function to set
                // *m to NULL
}

通过使用这些局部变量,代码比free(*((*m)->ptr + i));之类的东西更容易阅读

对于分配代码中的 break...

您没有在前两个 malloc 之后检查 NULL 然后在循环内进行检查,这有点奇怪(错误?)。此外,使用 break 有点奇怪,因为它会使其余的指针未初始化。

无论如何 - 如果您真的希望在分配代码中使用 break,则在释放内存时还需要考虑到这一点。类似的东西:

void destroy_image(struct image_t **m) {
    if (m == NULL) return;
    if (*m == NULL) return;
    struct image_t *t = *m;
    int **ptr = t->ptr;
    if (ptr != NULL)
    {
        for (int i = 0; i < t->height && ptr[i] != NULL; i++) 
        {
            free(ptr[i]);
        }
        free(ptr);
    }
    free(t);
    *m = NULL;
}

答案 1 :(得分:2)

为了让您的 destroy 函数正常工作,指针数组中的所有指针必须有效或为空。 malloc() 返回的内存未初始化,因此在分配函数中退出循环会使指针数组的其余部分未初始化,因此不应传递给 free()

另请注意,您应该测试结构指针和指针数组的分配失败。此外,新分配结构的 widthheight 成员未初始化:您应该使用函数参数来初始化它们。

destroy_image 函数可能应该在释放后将 *m 设置为 NULL,并且即使 free(*m); 是空指针也必须(*m)->ptr

以下是针对此问题的解决方案:

  • 使用 calloc() 分配数组(在所有情况下都是一个好主意)或
  • height 设置为成功分配的指针数。
  • 将数组中剩余的指针显式设置为 NULL
  • 在分配失败时释放分配的块并返回 NULL

这是修改后的版本:

#include <stdlib.h>

struct image_t {
    char type[3];
    int **ptr;
    int width;
    int height;
};

struct image_t *allocate_image(int width, int height) {
    struct image_t *struktura = calloc(1, sizeof(*struktura));

    if (struktura == NULL)
        return NULL;

    // should initialize struktura->type too
    struktura->width = width;
    struktura->height = height
    struktura->ptr = calloc(height, sizeof(*struktura->ptr));
    if (struktura->ptr == NULL) {
        free(struktura);
        return NULL;
    }

    for (int i = 0; i < height; i++) {
        struktura->ptr[i] = calloc(sizeof(*struktura->ptr[i]), width);
        if (struktura->ptr[i] == NULL) {
            // Iterate downwards on index values of allocated rows
            // (i --> 0) is parsed as (i-- > 0)
            // this test works on signed and unsigned index types, unlike (--i >= 0)
            while (i --> 0) {
                free(struktura->ptr[i]);
            }
            free(struktura->ptr);
            free(struktura);
            return NULL;
        }
    }
    return struktura;
}

void destroy_image(struct image_t **m) {
    if (m != NULL) {
        struct image_t *p = *m;
        
        if (p != NULL) {
            if (p->ptr != NULL) {
                for (int i = 0; i < p->height; i++) {
                    free(p->ptr[i]);
                }
                free(p->ptr);
            }
            free(p);
            *m = NULL;
        }
    }
}

答案 2 :(得分:1)

这是不必要的复杂和低效的。看看Correctly allocating multi-dimensional arrays

您可以像这样更改结构:

struct image_t {
    char type[3];
    int* ptr; // placeholder pointer
    int width;
    int height;
};

然后像这样 malloc:

struct image_t* s = malloc(sizeof *s);
s->width  = width;
s->height = height;
s->ptr    = malloc( sizeof(int[width][height]) );

(记得检查每个malloc的结果,如果返回NULL则停止程序。)

然后像这样使用它:

int (*arr)[s->height] = (void*) s->ptr; // convert to a temporary 2D array pointer
...
arr[i][j] = whatever; // this enables 2D array access syntax

像这样释放它:

free(s->ptr);
free(s);