使用malloc分配的数组types和数组之间的区别

今天我用一些C代码帮助我的一个朋友,我发现了一些奇怪的行为,我无法解释他为什么会这样。 我们有一个整数列表TSV文件,每行一个int。 第一行是列表中的行数。

我们也有一个非常简单的“读取文件”的交stream文件。 第一行读到n,行数,然后有一个初始化:

int list[n] 

最后是一个fscanf的for循环。

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

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

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

一切都好,即使是非常大的n。

有人可以解释为什么会发生? 是什么导致segfault与int list [n],当我们开始使用list = malloc(n * sizeof(int))时停止?

这里有几个不同的部分。

首先是声明一个数组的区别

 int array[n]; 

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

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

第二个版本在这里工作的原因是通常编译C的实现细节。 通常情况下,C内存被分成几个区域,包括堆栈(用于函数调用和局部variables)和堆(用于malloc编译对象)。 栈一般比堆小得多, 通常是8MB的东西。 因此,如果你尝试分配一个巨大的数组

 int array[n]; 

那么你可能会超过堆栈的存储空间,导致段错误。 另一方面,堆通常有一个巨大的尺寸(比如,系统上的空闲空间),因此, malloc一个大对象不会导致内存不足的错误。

一般来说,要小心C中的变长数组。它们可以很容易地超过堆栈大小。 更喜欢malloc除非你知道尺寸很小,或者你确实只需要一小段时间的arrays。

希望这可以帮助!

 int list[n] 

堆栈中的 n整数分配空间,通常非常小。 在堆栈上使用内存比替代方法快得多,但是如果你分配巨大的数组或者进行太深的recursion,那么它很小,并且很容易溢出堆栈(即分配太多的内存)。 您不必手动释放以此方式分配的内存,当数组超出范围时由编译器完成。

另一方面, malloc堆中分配空间,与堆栈相比通常非常大 。 你将不得不在堆上分配更多的内存来用尽堆内存,但是在堆上分配内存要比在堆上分配内存慢得多,并且当你完成使用时你必须通过free来手动释放内存它。

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

堆栈是有限的,并没有太多的空间,而堆是更大的。

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

这个声明在栈上分配内存

  int list[n] 

malloc分配在堆上。

堆栈大小通常小于堆,所以如果你在堆栈上分配太多的内存,你会得到一个stackoverflow。

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

假设你在你的实现中有一个典型的实现,最有可能的是:

 int list[n] 

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

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

在堆上分配内存。

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

当你使用一个malloc进行分配时,内存是从堆中分配的,而不是从堆栈中分配的,而堆栈的大小是非常有限的。

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

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

  int array[n]; 

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

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

它是dynamic分配数组的一个例子,在运行时用户可以知道数组的大小。 数组将被分配在堆上。