数组类型和用 malloc 分配的数组的区别

Posted

技术标签:

【中文标题】数组类型和用 malloc 分配的数组的区别【英文标题】:Difference between array type and array allocated with malloc 【发布时间】:2012-05-21 11:28:02 【问题描述】:

今天我正在帮助我的一个朋友编写一些 C 代码,我发现了一些奇怪的行为,我无法向他解释为什么会发生这种情况。我们有一个包含整数列表的 TSV 文件,每行有一个 int。第一行是列表的行数。

我们还有一个带有非常简单的“readfile”的 c 文件。第一行读到n,行数,然后有一个初始化:

int list[n]

最后是 nfscanf 的 for 循环。

对于小的 n(直到 ~100.000),一切都很好。但是,我们发现当 n 很大时(10^6),会发生段错误。

最后,我们将列表初始化改为

int *list = malloc(n*sizeof(int))

一切顺利,即使n 很大。

有人可以解释为什么会这样吗?是什么导致了int list[n] 的段错误,当我们开始使用list = malloc(n*sizeof(int)) 时它停止了?

【问题讨论】:

正是我正在寻找的,我在 harckerank 数组操作问题中遇到了同样的问题。 【参考方案1】:

int list[n] 将数据存储在堆栈中,而 malloc 将其存储在堆中。

栈是有限的,空间不大,而堆要大很多。

【讨论】:

【参考方案2】:

int list[n] 是一个 VLA,它在堆栈上而不是在堆上分配。您不必释放它(它会在函数调用结束时自动释放)并且它分配得很快,但存储空间非常有限,正如您所发现的。您必须在堆上分配更大的值。

【讨论】:

【参考方案3】:

这个声明在栈上分配内存

    int list[n]

malloc 在堆上分配。

堆栈大小通常比堆小,因此如果您在堆栈上分配太多内存,则会出现堆栈溢出。

另见this answer for further information

【讨论】:

【参考方案4】:

当您使用malloc 进行分配时,内存是从堆而不是从堆栈分配的,堆栈的大小受到更多限制。

【讨论】:

【参考方案5】:
int list[n]

堆栈上为n 整数分配空间,这通常非常小。在堆栈上使用内存比替代方案快得多,但它非常小,如果您执行分配大数组或递归太深等操作,很容易溢出堆栈(即分配太多内存)。您不必手动取消分配以这种方式分配的内存,当数组超出范围时由编译器完成。

另一方面,

malloc中分配空间,与堆栈相比,通常非常大。您将不得不在堆上分配更多的内存来耗尽它,但是在堆上分配内存比在堆栈上分配内存要慢得多,并且您必须通过 free 手动解除分配使用它。

【讨论】:

“在堆栈上使用内存比其他方式快得多”,这里是指“分配”还是“访问”? AFAIK,堆栈分配要快得多,但它也适用于访问(读/写)吗?谢谢【参考方案6】:

这里有几个不同的部分。

首先是声明数组为的区别

int array[n];

int* array = malloc(n * sizeof(int));

在第一个版本中,您声明了一个具有自动存储期限的对象。这意味着该数组仅在调用它的函数存在时才存在。在第二个版本中,您将获得具有动态存储持续时间的内存,这意味着它会一直存在,直到它被 free 显式释放。

第二个版本在这里工作的原因是 C 通常如何编译的实现细节。通常,C 内存分为几个区域,包括堆栈(用于函数调用和局部变量)和堆(用于malloced 对象)。堆栈的大小通常比堆小得多;通常它是8MB之类的东西。因此,如果您尝试使用

分配一个巨大的数组
int array[n];

那么你可能会超出堆栈的存储空间,导致段错误。另一方面,堆通常具有很大的大小(例如,与系统上的可用空间一样多),因此mallocing 一个大对象不会导致内存不足错误。

一般来说,小心 C 中的可变长度数组。它们很容易超过堆栈大小。首选malloc,除非你知道它的大小很小,或者你真的只想要这个数组很短的时间。

希望这会有所帮助!

【讨论】:

很好的答案!我想知道速度是否也有差异? 由于引用局部性的影响,我怀疑堆栈分配的数组访问速度更快,而malloc 本身比仅仅碰撞堆栈指针要慢得多。但实际上,最好使用更适合手头任务的任何方法。 或者,如果你声明 int arr[1000000];在任何函数之外,它们会自动设置为零并将存储在堆中。 @DSOI__UNUNOCTIUM 这些阵列将具有静态存储持续时间。您确定它们会存储在堆上吗? 我之前已经分配了大小高达几十万的数组。我现在就测试一下。【参考方案7】:

假设您的实现中有一个典型的实现,很可能是:

int list[n]

堆栈上的已分配列表,其中:

int *list = malloc(n*sizeof(int))

在您的堆上分配内存。

在堆栈的情况下,它们可以增长到多大通常是有限制的(如果它们可以增长的话)。在堆的情况下仍然有一个限制,但这往往在很大程度上受到您的 RAM + 交换 + 地址空间的限制,这通常至少大一个数量级,如果不是更多的话。

【讨论】:

【参考方案8】:

如果您使用的是 linux,您可以将 ulimit -s 设置为更大的值,这也可能适用于堆栈分配。 当您在堆栈上分配内存时,该内存将一直保留到函数执行结束。如果您在堆上分配内存(使用 malloc),您可以随时释放内存(甚至在函数执行结束之前)。

一般来说,堆应该用于大内存分配。

【讨论】:

【参考方案9】:
   int array[n];

这是一个静态分配数组的例子,在编译时数组的大小是已知的。并且数组会被分配到栈上。

   int *array(malloc(sizeof(int)*n);

这是一个动态分配数组的例子,数组的大小将在运行时为用户所知。并且数组会被分配到堆上。

【讨论】:

以上是关于数组类型和用 malloc 分配的数组的区别的主要内容,如果未能解决你的问题,请参考以下文章

new与malloc的区别struct与class的区别

new和malloc的区别

细说new与malloc的10点区别

细说new与malloc的10点区别

数据结构与算法基础之malloc()动态分配内存概述

动态内存分配