繁体   English   中英

如何在 C 中正确释放动态分配的数组

[英]How to properly free a dynamically allocated array in C

我正在尝试编写一组函数来支持动态分配的数组,其中结构包含数组和其他元数据。 目标是将函数返回给用户,并且可以从函数中调用结构信息。 代码似乎工作得很好,直到我得到从堆中释放内存的函数。 由于我不明白的原因,代码因分段错误而失败,这表明free_vector函数中的变量vec未指向正确的地址。 但是,我已经使用打印语句验证它指向正确的地址。 我希望有人可以帮助我理解为什么free_vector函数不起作用,特别是free命令。 我的代码和实现如下所示。

typedef struct
{
    size_t allocated_length;
    size_t active_length;
    size_t num_bytes;
    char *vector;
} Vector;

void *init_vector(size_t num_indices, size_t num_bytes) {
    // Allocate memory for Vector struct
    Vector *vec = malloc(sizeof(*vec));
    vec->active_length = 0;
    vec->num_bytes = num_bytes;

    // Allocate heap memory for vector
    void *ptr = malloc(num_bytes * num_indices);
    if (ptr == NULL) {
        printf("WARNING: Unable to allocate memory, exiting!\n");
        return &vec->vector;
    }
    vec->allocated_length = num_indices;
    vec->vector = ptr;
    return &vec->vector;
}
// --------------------------------------------------------------------------------

int push_vector(void *vec, void *elements, size_t num_indices) {
    Vector *a = get_vector_data(vec);
    if(a->active_length + num_indices > a->allocated_length) {
        printf("TRUE\n");
        size_t size = (a->allocated_length + num_indices) * 2;
        void *ptr = realloc(a->vector, size * a->num_bytes);
        if (ptr == NULL) {
            printf("WARNING: Unable to allocate memory, exiting!\n");
            return 0;
        }
        a->vector = ptr;
        a->allocated_length = size;
    }
    memcpy((char *)vec + a->active_length * a->num_bytes, elements,
            num_indices * a->num_bytes);
    a->active_length += num_indices;
    return 1;
}
// --------------------------------------------------------------------------------

Vector *get_vector_data(void *vec) {
    // - The Vector struct has three size_t variables that proceed the vector
    //   variable.  These variables consume 24 bytes of daya.  THe code below
    //   points backwards in memory by 24 bytes to the beginning of the Struct.
    char *a = (char *)vec - 24;
    return (Vector *)a;
}
// --------------------------------------------------------------------------------

void free_vector(void *vec) {
    // Free all Vector struct elements
    Vector *a = get_vector_data(vec);
    // - This print statement shows that the variable is pointing to the
    //   correct data.
    printf("%d\n" ((int *)vec)[2]);
    // The function fails on the next line and I do not know why
    free(a->vector);
    a->vector = NULL;
    a->allocated_length = 0;
    a->active_length = 0;
    a->num_bytes = 0;
}

int main() {
    int *a = init_vector(3, sizeof(int));
    int b[3] = {1, 2, 3};
    push_vector(a, b, 3);
    // The code begins to fails here
    free_vector(a);
}

该程序存在未定义行为

init_vector的返回值是char **类型,一个指针到指针到字符

return &vec->vector;

转换为void *

main中,此值转换为int *

int *a = init_vector(3, sizeof(int));

然后,当传递给push_vector时,此值会转换回void *

push_vector中,该值被强制转换为char *以执行指针运算

memcpy((char *)vec + a->active_length * a->num_bytes, elements,
            num_indices * a->num_bytes);

此操作会覆盖vector成员中包含的malloc返回的原始指针。

在我的系统上,这会尝试从Vector结构中vector成员的位置开始将 12 个字节(三个int )写入内存。

Vector *vec
|                    &vec->vector
|                    |      
v                    v      
+------+------+------+------+-----+
|size_t|size_t|size_t|char *|?????|
+------+------+------+------+-----+

这会溢出,因为我的系统上的sizeof (char *)8

这是写入数据的错误位置。 写入数据的正确位置是*(char **) vec - 或者只是a->vector

如果写入没有直接使程序崩溃( UB ),这肯定会导致free被传递一个不是由malloccallocrealloc返回的指针值,或者指针值NULL


另外:在free_vector中,这个值也被转换为一个int *

printf("%d\n", ((int *)vec)[2]); /* added a missing semi-colon. */

此外,还不清楚free_vector是应该释放原始分配,还是只释放vector成员。 您确实竭尽全力将此处的结构归零。

尽管如此,您仍然存在内存泄漏 - 尽管很小。

void free_vector(void *vec) {
    Vector *a = get_vector_data(vec);
    /* ... */
    free(a); /* This has to happen at some point. */
}

请注意,您应该使用offsetof来计算结构中成员的位置。 24的静态偏移量假设有两件事可能成立:

  • sizeof (size_t)始终为8 (实际最小sizeof (size_t)2 ),并且
  • 该结构不包含满足对齐的填充(这似乎很可能给定形式,但并非严格正确)。

您在评论中链接的使用灵活的数组成员,而不是指针成员,这意味着整个数据(分配大小和向量)都存储在连续的内存中。 这就是&运算符在此实现中产生将数据复制到的有效位置的原因。

除此之外:链接的实现似乎被破坏了,因为它有效地使用sizeof从指向灵活数组成员的指针(例如, &((vector_container *) pointer_to_flexible_member)[-1] )中获取容器结构的基础,这不会考虑到尾随填充的可能性,这将导致比预期更大的偏移量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM