c-为什么在这种特殊情况下,在堆栈上分配一个大元素不会失败



在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;

这会引发崩溃。请注意,具体的行为将取决于您使用的编译器和您的优化设置。不同的编译器会以不同的方式分配变量,优化编译器通常会从函数中完全删除ifoo,因为它们不需要。某些编译器还会在某些配置下将变量初始化为垃圾值,以帮助调试。

分配堆栈空间只需要更改堆栈指针,它是一个寄存器。如果你分配了太多的堆栈空间,堆栈指针将指向一个无效的内存区域,当程序试图读取或写入这些地址时,它将出错。大多数操作系统都有"保护页",因此有效内存不会放在堆栈旁边,从而确保程序在大多数情况下成功崩溃。

以下是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

相关内容

  • 没有找到相关文章

最新更新