
本文中使用的示例程序:stack.zip。
顾名思义,堆栈溢出就是堆栈溢出。在进行数值运算时,我们经常要处理运算结果溢出的问题。数值运算的结果可能是上溢或下溢。但是栈的溢出显然只能是溢出,也就是栈空间用完了。提到“栈”这个概念的时候,不要忘了它的兄弟“堆”,不要把两者混淆。
那么,什么时候会用完呢?如果我们记住C程序中的局部变量是分配在堆栈中的,函数调用会占用一部分堆栈空间,那么我们就很容易构造出相应的测试用例。
1、定义局部变量占用空间太大导致的堆栈溢出。
C:\"更多stack_local.c
/*
*从堆栈中分配太多内存会导致堆栈溢出。
*/
#包括
int main(int argc,char *argv[])
{
int foo[1000000];
返回0;
}
c:\"cl stack _ local . c
针对80x86的微软32位C/C优化编译器版本14.00.50727.42
版权所有(C)微软公司。保留所有权利。
stack_local.c
Microsoft (R)增量链接器版本8.00.50727.42
版权所有(C)微软公司。保留所有权利。
/out:stack_local.exe
堆栈_本地.对象
c:\"stack _ local
出现一个异常对话框:stack-local.jpg。
2、递归函数调用导致堆栈溢出
C:\"更多stack_recursive.c
/*
*无限递归调用很快会导致堆栈溢出。
*/
#包括
静态void foo(void);
静态空栏(void);
int main(int argc,char *argv[])
{
foo();
返回0;
}
静态空foo(空)
{
bar();
}
静态空心条(空心)
{
foo();
}
c:\"cl stack _ recursive . c
针对80x86的微软32位C/C优化编译器版本14.00.50727.42
版权所有(C)微软公司。保留所有权利。
stack _递归. c
Microsoft (R)增量链接器版本8.00.50727.42
版权所有(C)微软公司。保留所有权利。
/out:stack_recursive.exe
stack_recursive.obj
C:\"堆栈_递归
节目无声地结束了。查看进程的返回值,可以发现它实际上是异常终止的。只是不会弹出stack_local这样的对话框。
c:\"echo % error level %
-1073741819
要想知道为什么这两个程序会有如此细微的差别,你可以参考它们的汇编代码。原来是_chkstk()在起作用,其中stack_local会导致_chkstk()失败,并在程序初始加载时触发异常。Stack_recursive可以正确加载并运行一段时间,然后会引起堆栈溢出并触发异常。
要正确处理堆栈溢出,请采用以下方法:
(1)修改我们的程序,避免无限递归或者太深的递归。我们可以把一些递归代码做成非递归的,比如经典的qsort,最好用非递归算法来实现,这样更符合实际。
(2)修改我们的程序,不要定义太大的局部变量,尤其是在定义大型结构和大型数组的时候。有时候我们可能会使用_alloca()这样的特殊函数直接在栈上分配空间,所以要多加注意。
(3)利用编译器的特性,将进程允许的堆栈大小设置得更大。例如,可以使用MSC中的/STACK参数开关。
(4)对于那些可能导致堆栈溢出的代码,采用微软的结构化异常处理或标准C异常处理机制,结合_resetstkoflw()。当然,如果不麻烦的话,我们也可以自己检测所用堆栈的大小,动态检测是否可能导致堆栈溢出,以避免可能出现的异常。









