指针数组与元素数组

4
今天早上我和一位同事讨论了这个话题。他说,将数组分配为指针数组总是更好的选择,因为逐个分配每个元素有更好的机会获得一个空闲的内存块。就像这样:
// Consider n_elements as a dynamic value
int n_elements = 10, i;
int **ary = (int **) malloc(sizeof(int *) * n_elements);

for(i = 0; i < n_elements; i++)
{
  ary[i] = (int *) malloc(sizeof(int));
}

与他的方法相反,我认为更好的方式是分配元素数组,因为这样你可以得到一块紧凑的内存块,而不是散布在堆中的大量引用。就像这样:

int n_elements = 10;
int *ary = (int *) malloc(sizeof(int) * n_elements);

ary[0] = 100;

经过这次对话,我一直在思考,并得出结论:这取决于具体情况。针对小数据类型,我认为第二种解决方案是更好的选择,原因如上所述;但是当分配大型结构体数组时,第一种方法可能更好。

除了我的结论,你对此有何看法?


2
就像你说的,“这取决于情况”。 :) - lurker
4
如果需要10个整数,使用int a[10];是最好的选择,做另外两种方法都有点儿疯狂。对于一个由N个项目组成的单一连续序列,后者(减去malloc()的强制类型转换)是最佳选择(很少不是这种情况)。前者只有在需要任意长度的任意长度列表的列表时才通常使用,在实际使用2D数组语法时提供语法(而实际上它却不是)。但是,这取决于您非常具体的需求。 - WhozCraig
1
这不是基于主观看法的。哈哈。 - Justin Meiners
@WhozCraig 这就是为什么我指出“将n_elements视为动态值” :) - Hardy
1
毫无疑问,所有理论的最高目标是使不可简化的基本元素尽可能简单且少,而无需放弃对单个经验数据的充分表示。——AE 1933 - chux - Reinstate Monica
1个回答

6

对于我所知的任何主流硬件(至少是一般情况下),他都是错的。可能会有一些特殊情况和些许变化,但在能够选择时,应该选择元素数组而不是指针数组。

CPU缓存喜欢数据紧密地打包在一起。分别分配每个元素将增加缓存未命中率、减慢分配时间并浪费内存(由于分配对齐)。 CPU速度和内存之间的差距每年都在增长,这增加了紧密打包数据和批处理操作的收益。

你应该阅读此问题描述的文档《每个程序员都应该了解的关于内存的事情》。它详细描述了现代CPU / Memory关系的所有细节以及为什么连续数据非常重要。


2
而且,对于用户定义的类型,如果过大可能导致内存碎片化。 - Uchia Itachi
3
唯一的例外是大型、访问稀疏的数组,在这种情况下,最好是每个条目被分配时能够靠近其他它所引用的内容。但这取决于堆能否按时间和地址的接近性进行分配。 - Hot Licks
2
还应该注意到,许多缓存每个分配都有一个缓存头,因此单独分配多个条目会占用更多的堆。此外,将舍入到分配边界会为单独的分配占用更多的堆。 - Hot Licks
@HotLicks 是的,我刚想起来并提到了它。谢谢。 - Justin Meiners
1
@JustinMeiners,这正是我所期望听到的。我会查看这份文档。谢谢! - Hardy
1
糟糕——我之前说的是“缓存”,但应该是“堆”。每个分配通常都有一个头部,加上舍入到分配边界。这可能会为一个分配增加32字节或更多。 - Hot Licks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接