先说一下问题现场。
代码如下
free(volthisframe->Tops); free(volthisframe->Bots); printf("what\n"); free(volthisframe->SCTops); free(volthisframe->SCBots);
运行时出现段错误,“what”随机出现,也就是说可能是打印前出现段错误,也可能是打印后出现段错误。但最终定位就在这几行free里面。
人格保证,各个指针都有初始化,都有检查,代码这里没有列出来而已。
困扰了我2,3天时间,因为完全无法解释free出现段错误这个问题背后的原因,指针是绝对正常的。后来查阅了大量资料,终于理了个脉络出来,大概知道怎么回事了。进一步的debug还在进行中。。。
提醒:这只是泛泛而谈,因为内存管理的问题跟语言、编译器、操作系统都有关系,windows下和linux就大不相同,本文也没有想精确定位,只是给个思路而已。
下面是具体分析,首先是参考资料
参考资料
编号 |
名称 |
作者 |
内容 |
1 |
http://baike.baidu.com/link?url=-1euetbsnxgUONmVs3jA5eexMlkm5GJVTRIrcn1z3KwFVrDT-Ei6qtcxKxWAm229u6pWvOq0KKnWuWogahdeoK |
“堆栈”的百度百科,杂七杂八讲了一大堆东西。 |
|
2 |
http://imatlab.lofter.com/post/286ffc_a6ead7 |
局部变量数组过大造成的段错误 |
|
3 |
http://www.oschina.net/question/617142_65867?sort=default&p=1#answers |
典型的数组越界 |
|
4 |
http://blog.csdn.net/misskissc/article/details/10757975 |
子函数返回的时候,数据区、栈区的内容是否有效的问题。 |
|
5 |
http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html |
各种段错误及其调试方法 |
|
6 |
http://bbs.chinaunix.net/thread-1332271-1-1.html |
局部变量作为实参传递出现错误 |
|
7 |
http://blog.csdn.net/wind19/article/details/5964090 |
C中的内存管理, |
|
8 |
http://blog.csdn.net/wind19/article/details/5964137 |
C中堆和栈的数组越界,局部变量是放在栈中的 |
|
9 |
http://www.cs.ucsb.edu/~pconrad/cs16/10W/extraLabs/el01/ |
UCSB的C语言课程关于段错误的实验内容 |
这个问题跟C语言和编译器的内存分配,linux的内存管理都有关系,一个一个来。
出现段错误的情况,基本还是栈溢出(使用的内存空间大于系统默认分配的栈空间)[2][5]
数组越界[3]
访问不存在的地址(指针NULL)[5]
访问系统保护的内存地址[5]
访问只读的内存地址[5]
关于入栈、出栈 [1]讲了一点,在子函数调用的时候的情况,注意这只是多数情况,具体的系统很可能不一样
栈: 在函数调用时,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向函数的返回地址,也就是主函数中的下一条指令的地址,程序由该点继续运行。 |
[5]里面讲了很复杂的调试方法,但用在IDE里面用gdb可以很方便找到位置。关键是linux下,对于段错误的报警存在可能的偏差!
比如[3],在数组越界的时候并未实时报警,而是在free的时候才报警。
实际上我也是这种情况,应该是volthisframe->Tops和Bots都越界了,但是到free的时候才随机选一个报警。甚至是下一个free才报警。定位都很困难,定位到free也不解决任何问题。。。
OK,总结如下,主要是[7][8]说的最清楚
|
BSS、数据段、代码段是编译器预分配的, BSS段即未初始化的全局变量和静态变量的空间,读写。运行时初始化 数据段即已初始化的全局变量和静态变量的空间,读写。已由编译器初始化 代码段,就是代码空间,有的编译器和系统允许修改这一部分。 堆栈都由系统分配。 堆:很多系统都是从虚拟内存里面直接给,大小几乎不限。malloc和free类的内存空间就在这里。 栈,这个是重点 系统指定的空间,每个程序大小是统一的。linux下默认是8M。用ulimit查看修改。 局部变量就放在这里面,如果出现子函数调用的情况,除了子函数的局部变量和子函数的实参(其实也是局部变量)之外,父函数的返回地址也会入栈[1]。返回时如有返回值,也会压入栈中。调用时的入栈顺序是: 返回地址、实参、局部变量。 出栈相反。 |
栈就会带来很多问题。看了[4],我再总结如下:
局部变量的有效区间:本函数的存在时空。本函数返回之后,栈的内容其实还在,但随时可能被其它函数入栈顶替。
局部变量的地址作为实参传入:入栈的是父函数的局部变量的地址,其实是可以的,因为这时候父函数肯定还没结束。栈内空间依然保留,而且可读写。
根据[7],局部变量数组越界其实是在栈里面,而全局变量数组越界是在数据段或者BSS段里面。
而堆里面越界是在系统的虚拟内存里面。
Bingle!
最终原因:
很可能这是造成堆里面越界不能及时反映的原因,等到free的时候系统才发现不对头。因为使用的时候系统也没有随时监视,系统无法得知。
但无法理解系统为啥free这时候会发现不对,free里面可能有检查。猜测有记录使用了那些堆。
所以,我应该是指针在过程中使用的时候出现越界的情况,但在free时系统才报错,而且还不一定在哪儿报,可能是越界的指针本身free时,也可能是被侵犯的指针free时。我这里就是volthisframe->Tops和volthisframe->Bots两个指针都越界了(这两个是同步用的),但有时会在free(volthisframe->SCTops)时报错!
由于是嵌入式,为节省内存空间,我的指针使用极度繁杂,这下够的我搞!