【问题标题】:C: Which is better? Malloc array of pointers to structures, or array of structures?C:哪个更好? Malloc 指向结构的指针数组,还是结构数组?
【发布时间】:2012-02-25 15:13:49
【问题描述】:

我一直对此感到好奇,当在数组内部使用结构时,就内存分配而言,是为数组中的每个条目分配一个新结构更好,还是更好在数组中为 N 个结构分配足够的空间。

//pointer based:
struct myStructure ** tmp = malloc(sizeof(struct myStructure *) * N);
tmp[0] = malloc(sizeof(struct myStructure));
tmp[0]->whatever = true;

//or structure in the array:
struct myStructure * tmp = malloc(sizeof(struct myStructure) * N);
tmp[0].whatever = true

其中有什么好处?我觉得使用第二种形式是更好的做法,因为您最终会得到更少的小型 malloc 调用,但在某些情况下您可能只能使用第一种方法。

对此有何见解?

谢谢!

【问题讨论】:

    标签: c memory malloc


    【解决方案1】:

    目前有三个非常好的答案为什么你应该使用第二种方法,所以我不会重复他们的答案。我确实想说第一种方法有几个好处:

    • 第一个数组更容易增长收缩根据他们在系统中的实际需要。增加第一个指针数组非常容易——每个元素总共有 4 或 8 个字节长,因此将数组大小加倍不会太多花费太多。

      实际结构的第二个数组可能要大得多(sizeof struct foo 乘以元素数量),如果 realloc(3) 没有足够的可用空间来处理,即使稍微扩大数组也可能会耗尽内存。

    • 第一个数组使您能够通过“句柄”引用系统中的对象,并根据需要重新排列它们的内存。您可以在页面大小的slabs 中分配底层对象并将对象重新压缩到几乎完整的平板上——允许您将页面返回给操作系统以供以后使用。系统中的其他对象必须通过另一层间接来获取被引用的对象,但这些客户端对象(“引用”?)在您移动对象时不需要更新其指针。

    • 第一个数组中对象的生命周期是“解耦的”——其中一些对象可能存在很长时间,而其他对象可能只存在几毫秒。在第二个数组中,整个数组具有相同的生命周期。您可以向第二个数组添加一个额外的数据结构来管理哪些对象是活动的,哪些是死的,或者在结构中添加新的字段来指示哪些是活动的和死的,但是这两种方法都需要更多的工作。对于第一个数组,如果指针不是NULL,则对象是活动的。

    这两种方法都有其优点。为手头的工作选择合适的。

    【讨论】:

      【解决方案2】:

      数组中的结构通常更好;你应该使用它,除非你有理由使用其他形式。

      从代码正确性的角度来看,您应该处理 malloc() 失败。如果循环中有 1000 个 malloc(),则更有可能在错误处理代码中出现编程错误。同样,如果你的数据结构更复杂,你就更有可能泄露一些东西。所以单个 malloc() 更容易。

      从分配速度的角度来看,malloc() 显然需要时间来运行,所以单个 malloc() 通常会更快。

      从内存大小的角度来看,malloc() 通常在每次分配上都有一些开销。显然,指针是额外的成本。因此,如果您分配 1000 个 16 字节结构,您最终可能会在 malloc 开销中得到每个结构 16 字节和一个 8 字节指针,总共 40,016 字节。进行一次分配只会占用 8,016 字节。

      从访问速度的角度来看,单个数组可能会更快,尤其是在结构较小或您按顺序读取结构的情况下。如果结构很小,那么它们中的几个将适合单个高速缓存行,因此它们可以作为一个组进行读/写。如果按顺序读取结构,CPU 很可能会注意到对大数组的线性访问并将其预加载到缓存中。如果您使用指针数组和单独的分配,那么内存布局会更加随机,并且这些优化都不会起作用。此外,由于您正在访问更多数据(指针),因此您的算法将更快地从数据缓存中退出。

      从内存碎片的角度来看,这取决于。如果您有足够大的结构(占总 RAM 的很大一部分),那么您可能会遇到这样一种情况,即没有足够的连续可用内存来分配单个大数组,但有足够的空间来分配指针数组和个别结构。 (例如,如果您的操作系统上有一个 32 位程序,将您的内存限制为 2GB,并且您的内存分配器在内存的中途分配了其他东西,那么您不能执行单个 1.5GB 分配,但您可以执行15 100MB 分配)。这种情况很少见,因为人们通常不会处理那么大的数据。

      【讨论】:

        【解决方案3】:

        一般我会使用第二种方式,因为如果你使用所有插槽,它:

        • 它使用的内存略少(指针大小的 N 倍);
        • 碎片少了堆;
        • 避免 N 次调用 malloc/free(=>分配/解除分配更快更简单);
        • 在访问每个结构时避免双重间接(非常小的改进)。

        另一方面,如果您不打算使用数组的所有插槽(但您必须能够按需存储许多 structs)并且您的 struct 非常大,所以节省内存是值得的。此外,如果您需要廉价地更改 structs 的顺序,这可能是值得的(尽管您也可以通过使用分隔的指针数组来使用第二种方法)。

        【讨论】:

          【解决方案4】:

          通常,第二个更好 [IMO] - 它可能会更便宜[内存和时间],并且肯定更容易维护。

          但是,在某些情况下,第二种方法可能会失败 - 当第一种方法成功时。这可能是由于memory fragmentation 而发生的 - 您的所有结构都有足够的内存,它只是不在您内存中的“一个地方”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-06-04
            • 1970-01-01
            • 1970-01-01
            • 2013-08-02
            • 2013-04-18
            • 1970-01-01
            • 2012-04-18
            • 1970-01-01
            相关资源
            最近更新 更多