C 自由结构动态数组 - 为什么它们不是连续的



我正在努力解决一个C结构,它必须容纳一个动态的较小结构数组:

typedef struct issueStruct {
    int data;
} issue;
typedef struct volumeStruct {
    issue* collection;
    size_t elements;
} volume;

我可以在卷结构的数组中动态创建任意数量的问题结构。 我还可以遍历该数组:

int main(){
    volume* TimeMagazine = (volume*)malloc(sizeof(volume));
    TimeMagazine->collection = (issue*)malloc(4 * sizeof(issue));
    TimeMagazine->elements = 4;
    issue* ptr = TimeMagazine->collection;
    int i;
    // Populate & iterate through array:
    i = 0;
    while(i < TimeMagazine->elements){
            ptr->data = 100*i;
            printf("%d)  %dn", i, ptr->data);
            i++;
            ptr = ptr+i;       // Advance ptr
    }
    return 0;
}
OUTPUT:
[Linux]$ gcc -Wall magazines.c
[Linux]$ ./a.out
0)  0
1)  100
2)  200
3)  300
[Linux]$

目前为止,一切都好。 当我在 GDB 中逐步完成上述操作时,一切看起来都很好,尽管我注意到问题结构似乎没有连续的内存地址。 这是我看到的内存地址:

issue 0)  0x602030
issue 1)  0x602034
issue 2)  0x60203c
issue 3)  0x602048

这让我停顿了一下;我本来假设所有问题都会相隔 4 个字节,如sizeof(issue) = 4. 更严重的是,当我修改我的"遍历"代码以释放数组的元素时,我的代码 seg 出错了。 具体来说,当它试图释放第二个问题时,它会出错。 代码如下:

    i = 0;
    ptr = TimeMagazine->collection;
    issue* ptr2 = ptr;
    while(i< TimeMagazine->elements){
            printf("freeing %d...n", i);
            i++;
            free(ptr2);           // free ptr2
            ptr2 = ptr = ptr+i;   // advance ptr & ptr2
    }

这是错误(Linux上的GCC(:

*** Error in `./a.out': free(): invalid pointer: 0x000000000137c034 ***

所以我确定我在这里错过了一些东西,但不确定是什么。 有人可以推荐一种有效的方法来释放((数组元素吗?

非常感谢!

-皮特

PS - 有很多"释放数组中的结构"帖子,但似乎没有一个与我正在做的事情完全匹配。 所以我发布这个是希望我对这个问题的版本是独一无二的。

while(i < TimeMagazine->elements){
        ptr->data = 100*i;
        printf("%d)  %dn", i, ptr->data);
        i++;
        ptr = ptr+i;       // Advance ptr
}

您在ptr = ptr+i中使用了错误的指针算法,应该ptr = ptr+1或您在边界之外访问。free部分也是如此。

正如@kaylum在评论中指出的那样:您正在循环调用free,这也是错误的,您可以立即free(TimeMagazine->collection);,因为您正在为同一块中的4元素保留空间。

这是关于连续内存和包含动态数组的结构的旁注。有关实际答案,请参阅@KeineLust给出的答案。

如前所述,一个malloc==一个free

但是,未提及的是,由于缓存考虑,连续内存的性能通常更好。

这意味着,如果struct volumeStruct的内存和动态数组都使用相同的malloc调用进行分配,则性能会更好。

有两种

常见的方法可以实现此目的。

第一,使用您当前拥有的相同结构(我修复了您的循环以具有ptr = ptr + 1,因此我们不会越界(:

int main(){
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue)) );
    TimeMagazine->collection = TimeMagazine + 1; // pointer arithmetics
    TimeMagazine->elements = 4;
    issue* ptr = TimeMagazine->collection;
    int i;
    // Populate & iterate through array:
    i = 0;
    while(i < TimeMagazine->elements){
            ptr->data = 100*i;
            printf("%d)  %dn", i, ptr->data);
            i++;
            ptr = ptr+1;       // Advance ptr
    }
    free(TimeMagazine);
    return 0;
}

另一种选择(我认为这是在 C99 中引入的(,是在结构的末尾添加一个可变长度数组。这样可以节省collection指针所需的 8(或 4(个字节。

即:

typedef struct issueStruct {
    int data;
} issue;
typedef struct volumeStruct {
    size_t elements;
    issue collection[];
} volume;
int main(){
    volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue)) );
    TimeMagazine->elements = 4;
    // no need to assign a value for TimeMagazine->collection
    issue* ptr = TimeMagazine->collection;
    int i;
    // Populate & iterate through array:
    i = 0;
    while(i < TimeMagazine->elements){
            ptr->data = 100*i;
            printf("%d)  %dn", i, ptr->data);
            i++;
            ptr = ptr+1;       // Advance ptr
    }
    free(TimeMagazine);
    return 0;
}

最大的好处是CPU内存缓存和更简单的代码。在大多数情况下,我们为每个对象保存两个系统调用(一个malloc和一个free(这一事实是无关紧要的。

最新更新