【发布时间】:2021-05-01 10:26:46
【问题描述】:
如果你想分配一个结构数组,你可以通过声明类似的东西来静态地完成它
struct myStruct myStructArray[100];
或者像
这样的东西struct myStruct *myStructArray = calloc(100, sizeof(struct myStruct) );
但在这种情况下,您有责任释放内存。
在许多应用程序和示例中,我发现了一种混合方法:
struct wrapperStruct
{
int myInt;
struct myStruct myStructArray[1];
};
然后分配是这样进行的
int n = 100;
size_t memory_size = sizeof(struct wrapperStruct) + (n - 1) * sizeof(struct myStruct);
struct wrapperStruct *wrapperStruct_p = calloc(1, memory_size);
所以(如果我理解正确的话)因为数组是结构的最后一个成员,并且结构的字段尊重内存中的相同位置,那么您将使用 99 个条目“扩展”单个条目数组 myStructArray。
这使您可以安全地编写wrapperStruct_p.myStructArray[44] 之类的内容,而不会导致缓冲区溢出,也无需创建动态分配的结构数组,然后在最后处理内存处理。所以替代方法是:
struct wrapperStruct
{
int myInt;
struct myStruct *myStructArray;
};
struct wrapperStruct *wrapperStruct_p = calloc(1, sizeof(struct wrapperStruct) );
wrapperStruct_p.myStructArray = calloc(100, sizeof(struct myStruct) )
问题是当您尝试释放 wrapperStruct_p 变量时会发生什么?
你是否造成了内存泄漏?
C 内存管理是否能够理解结构数组由 100 个条目而不是 1 个组成?
除了不必释放结构内的指针之外,第一种方法有什么好处?
【问题讨论】:
-
我要注意的一点是,使用
struct myStruct myStructArray[1];的方法实际上是在伪造flexible array member,这是我不建议这样做的(如果它完全合法的话)。这回答了“C 内存管理是否能够理解结构数组由 100 个条目而不是 1 个组成?”在某种程度上,因为使用 FAM 可以清楚地表明它不仅仅是 1 个元素,而且元素的数量存储在其他地方。这是因为,在 C 中,内存管理器就是程序员。 -
要在现代 C 中正确使用声明灵活数组成员,请使用
[]而不是[1]。 (在 GCC 中,您可以使用它的扩展名[0]。不应在新代码中使用它。)如果您使用[1],编译器可能会假定数组只有一个元素,无论您分配多少,所以优化可以将p->member[complicated expression]之类的表达式转换为p->member[0],因为零是一个元素的数组索引的唯一定义值(因此它是避免计算表达式的有效优化),这会破坏您的程序。 -
@EricPostpischil 我一直以为
struct myStruct *myStructArray这个表达式等价于struct myStruct myStructArray[]。我认为在这两种情况下你最终都会得到一个未启动的指针。因此,如果您需要使用calloc或malloc分配两者,您将拥有堆内存。对吗? -
@Bemipefe:将成员声明为
struct myStruct *myStructArray会在结构中创建一个指针。要使用它,您为结构分配内存,为struct myStruct数据单独分配内存,并将指针设置为指向该内存。当一个成员被声明为struct myStruct myStructArray[]时,它告诉编译器,当为结构分配空间时,它可能包含用于struct myStruct数据的额外空间,这些额外元素将从结构中@987654345 所在的位置开始@是。 -
@EricPostpischil 好的,谢谢。因此,如果我理解正确,当您使用
[]代替*时,数组将存储在结构的同一区域中。我想知道如果您声明像struct wrapperStruct ws;这样的变量会发生什么。在这种情况下,结构存储在堆栈中,应扩展堆栈以存储数组。我想知道是否可以达到限制。我的意思是我需要分配struct wrapperStruct *ws以便将结构和指针存储在堆中吗?