在C中的堆栈上分配int
和大数组时,程序执行时不会出错。然而,如果我事先初始化堆栈上的变量,它会以segfault崩溃(可能是因为大数组超过了堆栈大小)。如果在声明数组之后初始化变量,这对我来说是有意义的。是什么导致了这种行为,就记忆而言?
我的印象是,只需在堆栈上声明一个变量,就可以分配所需的空间,导致在分配非常大的数据类型时立即崩溃。
我怀疑这与编译器优化它有关,但考虑到我在第二个例子中也没有更改foo
,这没有意义。
我使用gcc 7.2.0进行编译,没有设置任何标志。在Ubuntu 17.10上执行。
这运行时没有错误:
int main(){
int i;
unsigned char foo [1024*1024*1024];
return 0;
}
而这会立即崩溃:
int main(){
int i = 0;
unsigned char foo [1024*1024*1024];
return 0;
}
有人能告诉我这里发生了什么吗?
注意:以下是实现细节。C标准不包括这一点。
崩溃不是由分配空间引起的。崩溃是由写入不可写的页面或读取不可读的页面引起的。
您可以看到,声明实际上不需要读取或写入任何内存,不一定:
int i;
但如果它被初始化,你必须写下值:
int i = 0;
这会引发崩溃。请注意,具体的行为将取决于您使用的编译器和您的优化设置。不同的编译器会以不同的方式分配变量,优化编译器通常会从函数中完全删除i
和foo
,因为它们不需要。某些编译器还会在某些配置下将变量初始化为垃圾值,以帮助调试。
分配堆栈空间只需要更改堆栈指针,它是一个寄存器。如果你分配了太多的堆栈空间,堆栈指针将指向一个无效的内存区域,当程序试图读取或写入这些地址时,它将出错。大多数操作系统都有"保护页",因此有效内存不会放在堆栈旁边,从而确保程序在大多数情况下成功崩溃。
以下是Godbolt:的一些输出
main:
push rbp
mov rbp, rsp
sub rsp, 1073741720 ; allocate space for locals
mov DWORD PTR [rbp-4], 0 ; initialize i = 0
mov eax, 0 ; return value = 0
leave
ret
请注意,此版本不会崩溃,因为i
被放置在堆栈的顶部(向下增长)。如果i
被放置在堆栈的底部,这很可能会崩溃。编译器可以自由地将变量按任何顺序放在堆栈上,因此它是否真的崩溃在很大程度上取决于您使用的特定编译器。
您还可以更清楚地看到分配不会崩溃的原因:
; Just an integer subtraction. Why would it crash?
sub rsp 1073741720