我正在努力解决一个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
(这一事实是无关紧要的。