C 自由动态结构数组 - 为什么它们没有传染性
C Free Dynamic Array of Structs - Why are they not contigous
我正在努力处理一个必须包含较小结构的动态数组的 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) %d\n", 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
。更严重的是,当我修改 "iterate through" 代码以释放数组元素时,我的代码出现段错误。具体来说,它在尝试释放第二个问题时出错。这是代码:
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 - 有很多 "freeing structs in array" 帖子,但 none 似乎与我正在做的完全吻合。所以我发布这个是希望我的这个问题的版本是独一无二的。
while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+i; // Advance ptr
}
您在 ptr = ptr+i
中使用了错误的指针算法,应该是 ptr = ptr+1
或者您访问了边界之外。 free
部分相同。
正如@kaylum 在评论中指出的那样:您在循环中调用 free
,这也是错误的,您可以立即 free(TimeMagazine->collection);
因为您正在保留 space对于同一块中的 4
个元素。
这是关于连续内存和包含动态数组的结构的旁注。实际答案参考.
如前所述,一个malloc
==一个free
。
但是,未提及的事实是,出于缓存方面的考虑,连续内存通常性能更好。
这意味着如果使用相同的 malloc
调用分配内存和动态数组,您的 struct volumeStruct
会表现得更好。
有两种常见的方法可以实现这一点。
第一,使用与您当前相同的结构(我将您的循环固定为 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) %d\n", 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) %d\n", i, ptr->data);
i++;
ptr = ptr+1; // Advance ptr
}
free(TimeMagazine);
return 0;
}
最大的好处是 CPU 内存缓存和更简单的代码。在大多数情况下,我们为每个对象保存两个系统调用(一个 malloc
和一个 free
)这一事实是无关紧要的。
我正在努力处理一个必须包含较小结构的动态数组的 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) %d\n", 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
。更严重的是,当我修改 "iterate through" 代码以释放数组元素时,我的代码出现段错误。具体来说,它在尝试释放第二个问题时出错。这是代码:
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 - 有很多 "freeing structs in array" 帖子,但 none 似乎与我正在做的完全吻合。所以我发布这个是希望我的这个问题的版本是独一无二的。
while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+i; // Advance ptr
}
您在 ptr = ptr+i
中使用了错误的指针算法,应该是 ptr = ptr+1
或者您访问了边界之外。 free
部分相同。
正如@kaylum 在评论中指出的那样:您在循环中调用 free
,这也是错误的,您可以立即 free(TimeMagazine->collection);
因为您正在保留 space对于同一块中的 4
个元素。
这是关于连续内存和包含动态数组的结构的旁注。实际答案参考
如前所述,一个malloc
==一个free
。
但是,未提及的事实是,出于缓存方面的考虑,连续内存通常性能更好。
这意味着如果使用相同的 malloc
调用分配内存和动态数组,您的 struct volumeStruct
会表现得更好。
有两种常见的方法可以实现这一点。
第一,使用与您当前相同的结构(我将您的循环固定为 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) %d\n", 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) %d\n", i, ptr->data);
i++;
ptr = ptr+1; // Advance ptr
}
free(TimeMagazine);
return 0;
}
最大的好处是 CPU 内存缓存和更简单的代码。在大多数情况下,我们为每个对象保存两个系统调用(一个 malloc
和一个 free
)这一事实是无关紧要的。