为何大型局部字符数组未触发Segment Fault(段错误)?
问题:大数组局部变量未崩溃,编译器采取了什么处理机制?
我们所学知识表明局部变量在栈上分配,不能占用过多栈空间,但以下代码示例中的程序并未崩溃,请问编译器采取了什么处理机制?
#include<iostream> using namespace std; int main() { char arr[40960]; for(auto i =0;i<40960;++i) arr[i]='c'; std::cout<<sizeof(arr)<<std::endl; return 0; }
gcc --version
gcc (GCC) 14.1.1 20240522
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
反汇编代码
0000000000001159 <main>: 1159: 55 push %rbp 115a: 48 89 e5 mov %rsp,%rbp 115d: 48 81 ec 20 a0 00 00 sub $0xa020,%rsp 1164: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 116b: 00 00 116d: 48 89 45 f8 mov %rax,-0x8(%rbp) 1171: 31 c0 xor %eax,%eax 1173: c7 85 ec 5f ff ff 00 movl $0x0,-0xa014(%rbp) 117a: 00 00 00 117d: eb 17 jmp 1196 <main+0x3d> 117f: 8b 85 ec 5f ff ff mov -0xa014(%rbp),%eax 1185: 48 98 cltq 1187: c6 84 05 f0 5f ff ff movb $0x63,-0xa010(%rbp,%rax,1) 118e: 63 118f: 83 85 ec 5f ff ff 01 addl $0x1,-0xa014(%rbp) 1196: 81 bd ec 5f ff ff ff cmpl $0x9fff,-0xa014(%rbp) 119d: 9f 00 00 11a0: 7e dd jle 117f <main+0x26> 11a2: be 00 a0 00 00 mov $0xa000,%esi 11a7: 48 8d 05 92 2e 00 00 lea 0x2e92(%rip),%rax # 4040 <_ZSt4cout@GLIBCXX_3.4> 11ae: 48 89 c7 mov %rax,%rdi 11b1: e8 7a fe ff ff call 1030 <_ZNSolsEm@plt> 11b6: 48 8b 15 03 2e 00 00 mov 0x2e03(%rip),%rdx # 3fc0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GLIBCXX_3.4> 11bd: 48 89 d6 mov %rdx,%rsi 11c0: 48 89 c7 mov %rax,%rdi 11c3: e8 78 fe ff ff call 1040 <_ZNSolsEPFRSoS_E@plt> 11c8: b8 00 00 00 00 mov $0x0,%eax 11cd: 48 8b 55 f8 mov -0x8(%rbp),%rdx 11d1: 64 48 2b 14 25 28 00 sub %fs:0x28,%rdx 11d8: 00 00 11da: 74 05 je 11e1 <main+0x88> 11dc: e8 6f fe ff ff call 1050 <__stack_chk_fail@plt> 11e1: c9 leave 11e2: c3 ret
解答
核心原因:数组大小未超出栈空间上限
Linux系统默认栈空间大小通常为8MB左右,你定义的数组大小是40960字节(约40KB),远小于这个上限,因此栈有足够空间分配该数组,不会触发栈溢出崩溃。反汇编验证栈分配逻辑
从反汇编的sub $0xa020,%rsp指令可以看出,编译器确实在栈上为数组分配了空间:0xa020换算为十进制是40992字节,其中40960字节用于数组,剩余32字节用来存放栈保护变量、循环变量等栈帧额外数据。栈溢出保护机制的作用
反汇编中还包含gcc默认开启的-fstack-protector栈溢出保护逻辑:
mov %fs:0x28,%rax将线程本地存储的随机保护值存入栈帧- 函数返回前通过
sub %fs:0x28,%rdx检查保护值是否被篡改 - 若检测到溢出则调用
__stack_chk_fail终止程序
但你的代码中数组没有越界访问,因此不会触发该保护机制。
补充:如果将数组大小修改为远大于8MB(比如10MB),程序会因栈空间不足崩溃;也可以通过ulimit -s命令查看或修改当前系统的栈大小限制。
内容的提问来源于stack exchange,提问作者Rain
相关产品推荐
相关产品推荐

