74

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

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

int list[n]

最后是n带有 a的 for 循环fscanf

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

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

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

一切都很好,即使非常大n

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

4

9 回答 9

170

这里有几个不同的作品。

第一个是将数组声明为的区别

int array[n];

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

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

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

int array[n];

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

一般来说,小心 C 中的可变长度数组。它们很容易超过堆栈大小。malloc除非您知道大小很小,或者您确实只需要在短时间内使用该数组,否则请优先选择。

希望这可以帮助!

于 2012-05-13T21:56:19.620 回答
15
int list[n]

在堆栈n上为整数分配空间,通常非常小。在堆栈上使用内存比其他方法快得多,但它非常小,如果您执行诸如分配巨大数组或递归太深之类的事情,很容易溢出堆栈(即分配太多内存)。您不必手动释放以这种方式分配的内存,它是由编译器在数组超出范围时完成的。

malloc另一方面,在堆中分配空间,与堆栈相比,这通常非常大。您将不得不在堆上分配更多的内存来耗尽它,但是在堆上分配内存比在堆栈上分配内存要慢得多,并且您必须free在使用完毕后手动取消分配它.

于 2012-05-13T21:56:13.580 回答
3

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

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

于 2012-05-13T21:55:19.957 回答
1

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

通常,堆应该用于大内存分配。

于 2012-05-14T04:41:09.437 回答
1

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

于 2012-05-13T21:55:26.617 回答
1

此声明在堆栈上分配内存

    int list[n]

malloc 在堆上分配。

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

另请参阅此答案以获取更多信息

于 2012-05-13T21:55:31.373 回答
1

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

int list[n]

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

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

在你的堆上分配内存。

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

于 2012-05-13T21:56:35.753 回答
0

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

于 2012-05-13T21:55:54.483 回答
0
   int array[n];

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

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

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

于 2016-07-31T14:22:19.937 回答