【问题标题】:Dinamically vs Static memory allocation for a struct array结构数组的动态或静态内存分配
【发布时间】: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[]。我认为在这两种情况下你最终都会得到一个未启动的指针。因此,如果您需要使用callocmalloc 分配两者,您将拥有堆内存。对吗?
  • @Bemipefe:将成员声明为struct myStruct *myStructArray 会在结构中创建一个指针。要使用它,您为结构分配内存,为struct myStruct 数据单独分配内存,并将指针设置为指向该内存。当一个成员被声明为struct myStruct myStructArray[] 时,它告诉编译器,当为结构分配空间时,它可能包含用于struct myStruct 数据的额外空间,这些额外元素将从结构中@987654345 所在的位置开始@是。
  • @EricPostpischil 好的,谢谢。因此,如果我理解正确,当您使用[] 代替* 时,数组将存储在结构的同一区域中。我想知道如果您声明像struct wrapperStruct ws; 这样的变量会发生什么。在这种情况下,结构存储在堆栈中,应扩展堆栈以存储数组。我想知道是否可以达到限制。我的意思是我需要分配struct wrapperStruct *ws 以便将结构和指针存储在堆中吗?

标签: arrays c struct calloc


【解决方案1】:

问题是当您尝试释放 wrapperStruct_p 时会发生什么 变量?

你是否造成了内存泄漏?

最有可能,但不是必需的。内部动态数组的内存没有被释放,但如果你将指针地址保存到其他变量,你仍然可以稍后释放它。

C 内存管理是否能够理解结构数组由 100 个条目而不是 1 个组成?

“C 内存管理”负责堆栈和堆分配(后者使用systemcalls 所以也许它不是真正的“C 内存管理”),除了在汇编器(不像 Java 或其他垃圾收集语言)。

C 本身并不关心某处有多少条目以及您访问的内存的哪一部分(SEGFAULTS 是操作系统对内存访问违规的响应)

第一种方法除了不必 释放结构内的指针?

如果通过“第一种方法”您的意思是堆栈分配的数组,那么主要是您不需要分配任何东西并且堆栈为您完成它(缺点是它保持在声明的范围内分配并且您可以' t 释放或增加数组空间)然后恒定的分配速度和保证无论操作系统响应如何,您都将获得 100 个数组项(许多实时应用程序需要最大响应时间,因此堆分配可能是一个非常大的减速导致问题)。

如果您所说的“第一种方法”是指使用包装器结构,那么除了您已经说过的好处之外,我看不到任何好处。

我什至建议您不要提倡/使用这种方法,因为它是一种非常令人困惑的技术,并没有带来明显的好处(而且它分配了 1 个空间,即使它甚至可能没有被使用,但这是一个细节)

主要目标是编写其他人容易理解的代码。机器和编译器现在可以用代码创造奇迹,所以除非你是编译器设计师、标准库开发人员或机器级程序员对于嵌入式系统,您应该编写简单易懂的代码。

【讨论】:

  • 我喜欢最后一句话。开发人员通常不会过多考虑代码的可读性和可维护性。而像 C 语言中的 typedef 这样的东西会使问题恶化。
猜你喜欢
  • 2020-04-02
  • 2020-04-18
  • 2021-04-01
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多