堆叠溢位
外观
“堆叠溢位”的各地常用名称 | |
---|---|
中国大陆 | 堆栈溢出 |
台湾 | 堆叠溢位 |
港澳 | 堆叠溢位 |
堆栈溢出(英语:stack overflow)在电脑科学中是指使用过多的记忆体时导致呼叫堆叠产生的溢位[1],也是缓冲区溢出中的一种。堆叠溢位的产生是由于过多的函数呼叫,导致使用的呼叫堆叠大小超过事先规画的大小,覆盖其他记忆体内的资料,一般在递回中产生。堆叠溢位很可能由无限递回(Infinite recursion)产生,但也可能仅仅是过多的堆叠层级。
堆叠溢位在核心设计中尤其危险,因此很多入门核心设计教程建议使用者不要尝试使用递回程式;而是基于安全和效能考量,改用回圈处理问题。[2][3][4]
在POSIX相容平台上,堆叠溢位通常会造成作业系统产生SIGSEGV讯号。
递回溢位
[编辑]无限递回
[编辑]无限递回是堆叠溢位的最常见原因,如以下的C/C++语言程式会产生堆叠溢位:
int foo()
{
return foo(); //這裡出現无限重复的自我调用
}
然而有些语言(如Scheme)支援尾端递回优化,在这些语言中只有一般递回会产生堆叠溢位,而尾端递回不会:
(define (foo) (foo))
(define (foo) (+ (foo) 1))
这段代码中,前者(第一句)进入无穷回圈(Infinite loop),但不会产生堆叠溢位;后者(第二句)则会产生堆叠溢位。
防止堆叠溢位
[编辑]多数无限递回出现原因,都是基于程式本身没有错误检测机制:
int factorial( const int *const n ){
if ( *n == 0 )
return 1;
else
return *n * factorial( *n-- ); //這裡出現自我呼叫
};
如果在这里的n
是负数则会出现无限递回。其实,这段程式可以简单地加以修改,把n
的型别由整数改为非负整数即可解决:
unsigned int factorial( const unsigned int *const n ){
if ( *n == 0 )
return 1;
else
return *n * factorial( *n-- );
};
或者使用回圈处理:
unsigned int factorial( const unsigned int *const n ){
unsigned int i, result;
for ( i = *n, result = 1; i > 0 ; --i )
result *= i;
//自我呼叫部份改為迴圈處理
return result;
};
参看
[编辑]注释
[编辑]- ^ Chris Anley, John Heasman, Felix Lindner, Gerardo Richarte. The Shellcoder's Handbook: Discovering and Exploiting Security Holes. John Wiley & Sons. 2011. ISBN 1118079124 (英语).
- ^ Kernel Programming Guide: Performance and Stability Tips. Apple Inc. 2006-11-07. (原始内容存档于2008-12-07) (英语).
- ^ Dunlap, Randy. Linux Kernel Development: Getting Started (PDF). 2005-05-19. (原始内容 (PDF)存档于2012-02-27) (英语).
- ^ Coplien, James O. To Iterate is Human, to Recurse, Divine. C++ Report Vol 10. No.7. SIGS Publications Group: 43–51. 1998年7月 [2011-12-20]. (原始内容存档于2020-10-19) (英语).