[踩坑记录]对C编译器SegmentFault 11问题的思考及解决

做题中踩到的一些坑

[踩坑记录]对C编译器SegmentFault 11问题的思考及解决

前几天做到一道Mc插火把的题目(可能有些同学做过),需要开一个二维数组,题目没有给数组的范围,于是我就随意地写了一个5000,没想到问题就这么出现了。

在我使用gcc编译时,编译器并没有抛出异常,但运行的时候异常发生了。

报了一个 SegmentFault 11 的错误,并没有指出具体哪行出了错,令人匪夷所思。于是我打开了VScode进行调试。单步调试的过程中我发现当程序执行到main函数中异常被触发,如图所示。

从address我判断可能是内存地址分配错误导致的问题。但哪个地方会导致问题的发生呢?

问题初步分析

从编译器的角度讲,每定义一个新变量,编译器就会为其开辟一块内存空间,并有内存地址与其对应。所以问题应该就出现在变量的定义了。从我写的代码上看,只有数组需要为其开辟大小,其他均不用。问题很可能就在于我对二维数组的定义上。我尝试着缩小其范围,从5000缩小到2000,问题还是没有得到解决。我又再次将其缩小到1000,问题莫名其妙解决了。


深入分析

程序虽然正常运行,并被评测系统AC,但要问题还是没有真正解决,因为我并不知道其原因所在。Google大法好,我上Google搜寻了一波,终于从本质上弄懂问题。

开始分析:

在弄清楚问题之前需要了解堆栈的概念,相信大家不会陌生,这里引用百度百科上的基本定义

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

对于每个进程或线程来说,栈空间大小是有限的,而局部变量的内存是在栈上分配的,如果局部变量过大,则会出现分配失败的情况,也就是栈溢出。就好比一个桶只能装下5个苹果,在放第六个的时候就掉出来了。


解决之道

方法应该有很多,我了解到以下两种方法

1.把数组定义为全局变量

原因:全局变量分配时,会分配在全局变量空间,不受栈空间大小影响。

2.手动分配动态内存空间

通过malloc,calloc, zalloc等函数,可以分配动态内存空间。该空间会分配在堆上,同样不受栈空间的限制。代码会稍复杂。


END 之后就不会再跳进相同的坑里啦