今天我用一些C代码帮助了我的一个朋友,我发现了一些奇怪的行为,我无法解释他为什么会这样做。我们有一个带有整数列表的TSV文件,每行都有一个int。第一行是列表的行数。
我们还有一个带有非常简单的“readfile”的c文件。第一行读到n,行数,然后有一个初始化:
int list[n]
最后是带有fscanf的n循环。
对于小n(直到~100,000),一切都很好。但是,我们发现当n很大(10 ^ 6)时,会发生段错误。
最后,我们将列表初始化更改为
int *list = malloc(n*sizeof(int))
一切顺利,即使是非常大的n。
有人可以解释为什么会这样吗?是什么原因导致segfault与int list [n],当我们开始使用list = malloc(n * sizeof(int))时停止了?
答案 0 :(得分:136)
这里有几个不同的部分。
第一个是将数组声明为
之间的区别int array[n];
和
int* array = malloc(n * sizeof(int));
在第一个版本中,您声明一个具有自动存储持续时间的对象。这意味着只要存在调用它的函数,该数组就会存在。在第二个版本中,您将获得具有动态存储持续时间的内存,这意味着它将一直存在,直到使用free
显式释放它。
第二个版本在这里工作的原因是如何编译C的实现细节。通常,C内存分为几个区域,包括堆栈(用于函数调用和局部变量)和堆(用于malloc
ed对象)。堆栈的大小通常比堆小得多;通常它是8MB的东西。因此,如果您尝试使用
int array[n];
然后您可能会超出堆栈的存储空间,从而导致段错误。另一方面,堆通常具有巨大的大小(例如,系统上空闲的空间),因此malloc
大对象不会导致内存不足错误。< / p>
通常,请注意C中的可变长度数组。它们很容易超出堆栈大小。首选malloc
,除非您知道尺寸很小或者您确实只想在短时间内使用该阵列。
希望这有帮助!
答案 1 :(得分:8)
int list[n]
在堆栈上为n
个整数分配空间,这通常很小。在堆栈上使用内存比替代方案快得多,但是它非常小并且如果您执行诸如分配大型数组或进行过度递归之类的操作,则很容易溢出堆栈(即分配太多内存)。您不必手动释放以这种方式分配的内存,当阵列超出范围时由编译器完成。
malloc
在堆中分配空间,与堆栈相比,它通常非常大。您将不得不在堆上分配更大量的内存来耗尽它,但是在堆上分配内存要比在堆上分配内存要快得多,并且必须通过free
手动解除分配。你已经完成了它的使用。
答案 2 :(得分:2)
int list [n]将数据存储在堆栈中,而malloc将它存储在堆中。
堆栈是有限的,没有太多空间,而堆更大。
答案 3 :(得分:1)
int list[n]
是一个VLA,它在堆栈而不是堆上分配。您不必释放它(它在函数调用结束时自动释放)并且它快速分配但存储空间非常有限,正如您所发现的那样。您必须在堆上分配更大的值。
答案 4 :(得分:1)
此声明在堆栈上分配内存
int list[n]
malloc在堆上分配。
堆栈大小通常小于堆,因此如果在堆栈上分配太多内存,则会得到堆栈溢出。
答案 5 :(得分:1)
假设您的实施中有一个典型的实现,那么很可能是:
int list[n]
在您的堆栈上分配列表,其中:
int *list = malloc(n*sizeof(int))
在堆上分配内存。
在堆栈的情况下,通常限制这些可以增长的程度(如果它们可以增长)。在堆的情况下仍然有一个限制,但这往往会在很大程度上(大致)受到RAM +交换+地址空间的限制,这通常至少要大一个数量级,如果不是更多的话。
答案 6 :(得分:0)
使用malloc
进行分配时,内存是从堆中分配的,而不是从堆栈中分配的,这在大小上要受限制。
答案 7 :(得分:0)
如果您使用的是Linux,则可以将ulimit -s设置为更大的值,这也可以用于堆栈分配。 在堆栈上分配内存时,该内存将一直保留到函数执行结束。如果在堆上分配内存(使用malloc),则可以随时释放内存(甚至在函数执行结束之前)。
通常,堆应该用于大内存分配。
答案 8 :(得分:0)
int array[n];
这是静态分配数组的一个示例,在编译时,数组的大小将是已知的。并且数组将在堆栈中分配。
int *array(malloc(sizeof(int)*n);
这是动态分配的数组的示例,并且在运行时用户将知道数组的大小。并且数组将在堆上分配。