Nis*_*sky 5 c arrays stack segmentation-fault
int在C中在堆栈上分配一个大型数组时,程序执行时没有错误.但是,如果我事先在堆栈上初始化变量,则会发生段错误(可能是因为大型数组超出了堆栈大小).如果在声明数组后初始化变量,这对我来说是有意义的.是什么导致这种行为,记忆明智?
我的印象是,只需在堆栈上声明一个变量,就会分配所需的空间,导致在分配非常大的数据类型时立即崩溃.
我怀疑它与编译器优化它有关,但它没有意义,考虑到我foo在第二个例子中也没有改变.
我正在使用gcc 7.2.0进行编译,没有设置任何标志.在Ubuntu 17.10上执行.
这运行没有错误:
int main(){
int i;
unsigned char foo [1024*1024*1024];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这会立即崩溃:
int main(){
int i = 0;
unsigned char foo [1024*1024*1024];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
有人能告诉我这里发生了什么吗?
注意:以下是实现细节.C标准不包括此内容.
崩溃不是由分配空间引起的.崩溃是由于写入不可写的页面或从不可读的页面读取而导致的.
您可以看到声明实际上不需要读取或写入任何内存,不一定:
int i;
Run Code Online (Sandbox Code Playgroud)
但如果它已初始化,则必须写入值:
int i = 0;
Run Code Online (Sandbox Code Playgroud)
这会触发崩溃.请注意,确切的行为取决于您使用的编译器和您拥有的优化设置.不同的编译器会以不同的方式分配变量,以及优化的编译器通常会同时删除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
Run Code Online (Sandbox Code Playgroud)
请注意,此版本不会崩溃,因为i它位于堆栈的顶部(向下增长).如果i放在堆栈的底部,这可能会崩溃.编译器可以按任何顺序自由地将变量放在堆栈中,因此它是否真的崩溃将在很大程度上取决于您使用的特定编译器.
您还可以更清楚地看到分配不会崩溃的原因:
; Just an integer subtraction. Why would it crash?
sub rsp 1073741720
Run Code Online (Sandbox Code Playgroud)