本文转自徐飞翔的“c语言运行时出现segment fault的原因”版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
segment fault
段错误是在编程报错中经常出现的,特别是在c语言编程中,尤其常见,其原因本质上上是访问了非法(不属于这个程序)的内存地址空间,具体来说有以下几种情况:
- 局部变量定义中,使用了过大的局部变量,大于了系统给之的栈(stack)的大小,因此报错。比如以下代码在linux环境下,就可能出现段错误报错:
void foo(){ float vars[10000][10000]; }
代码很简单,就是在栈上划出了一个很大的连续内存空间,翻译成汇编如:
mov $100000016, %rsp # 还有多出的16个字节是上下文切换需要的内存,frame pointer %rbp, return address等, # 同时如果用gcc等编译的,还要考虑其内存对齐的要求,即是其是16字节的倍数。
这个栈大小可能超出了系统给定每个程序的栈的大小,可以通过shell命令
ulimit -s
进行查看系统给定的栈大小,比如笔者的就是:user@ubuntu: ulimit -s 8192
注意这里都是以1024字节(1KB)为单位的,因此默认的就是8MB的栈大小,如果你的程序需要更大的栈空间,那么可以通过
ulimit -s 1000000
类似这样的命令去重定义最大的栈大小。
stack overflow
栈溢出 C语言是没有对数组的边界检测的,这样在实际应用中常常会导致越界的问题,我们知道,程序的栈如下图所示:考察以下代码:
void foo(){ int vars[100]; vars[100] = 0; vars[101] = 0; vars[102] = 0; vars[103] = 0; }
当然,我们知道,我们对该数组
vars
只能检索到vars[99]
,因为其索引范围是[0,99]
,但是这种超出了理论上的索引边界(越界)的“低级错误”确是在实际代码中经常出现的bug的根源之一。从上图的程序栈我们可以看出,如果栈中的变量边界溢出,那么可能会对一些上下文信息,比如return address进行期望之外的修改,导致难以预料的错误(比如无法返回到调用函数,或者返回到不该返回的地址,这里容易被黑客进行栈溢出攻击),因此操作系统一般会检测这种段错误,同时报错segment fault
。