这段代码只是关于概念,没有任何意义。
void recur(int num)
{
static float tmp = num * num;
if (num == 0)
{
return;
}
else
{
recur(num - 1);
}
}
int main()
{
recur(1000000);
}
我认为静态变量仅在内存中使用一个位置,但是调用main中的recur函数会导致堆栈溢出故障,这确实有意义是否在堆栈中声明了变量tmp,但在堆栈中声明了静态变量不是堆栈,对吧?
tmp变量的行为是什么?
谢谢
答案 0 :(得分:4)
我认为静态变量仅在内存中使用一个位置
您的想法正确。
tmp变量的行为是什么?
tmp
变量只有一个对象。对recur
的所有调用都使用相同的对象。它在第一次调用该函数时初始化,并在main
返回后销毁。
每个函数调用都会压低堆栈-除非已通过扩展内联调用优化了该调用(例如在尾部调用优化的情况下),并且深度递归很容易使堆栈溢出。
请注意,您的函数还具有一个参数,该参数具有自动存储功能,可能会加速堆栈的消耗。
答案 1 :(得分:1)
静态变量不在堆栈中。您可以将其视为全局变量。因为每次调用都会将参数“ num”压入堆栈,所以会出现堆栈溢出。此外,返回地址位于堆栈上,因此即使是“空”功能也将导致溢出(来自https://en.wikipedia.org/wiki/Call_stack的图像)
答案 2 :(得分:1)
在C和C ++中,函数的静态变量和全局变量存储在内存的.data
部分中,该部分与堆栈和堆分开。
WiKi中有一个不错的diagram。
如先前的回答和评论所指出的,每次您呼叫recur
都是寄信人地址的地方,并且可能(取决于平台,我假设您正在x86上进行测试)参数在堆栈上分配。
您可以使用较小的递归(例如10次迭代)并打印出地址,以查看各节之间的差异,如下所示:
num
示例输出如下:
p:0x1c12c20 x:0x6011b8 z:0x7ffc0b6dd914
tmp:0x6011c8 num:0x7ffc0b6dd8fc
tmp:0x6011c8 num:0x7ffc0b6dd8dc
tmp:0x6011c8 num:0x7ffc0b6dd8bc
tmp:0x6011c8 num:0x7ffc0b6dd89c
tmp:0x6011c8 num:0x7ffc0b6dd87c
tmp:0x6011c8 num:0x7ffc0b6dd85c
tmp:0x6011c8 num:0x7ffc0b6dd83c
tmp:0x6011c8 num:0x7ffc0b6dd81c
tmp:0x6011c8 num:0x7ffc0b6dd7fc
tmp:0x6011c8 num:0x7ffc0b6dd7dc
tmp:0x6011c8 num:0x7ffc0b6dd7bc
答案 3 :(得分:0)
每次调用recur
函数都将需要分配至少num
个参数,这会占用自动存储空间。
答案 4 :(得分:0)
我已经看过主要编译器的汇编,通过最大程度的优化,您应该不会出现任何堆栈溢出:
因此,我想如果您为此微不足道的情况打开优化功能,则不会有堆栈溢出。
但是我想你的情况更复杂。
如果您的函数在没有任何静态变量的情况下不会导致任何堆栈溢出,然后在添加此静态变量的情况下导致堆栈溢出,则原因可能是对静态变量初始化的代码生成的副作用。
静态变量初始化由一种“互斥体”保护,以确保仅静态变量在多线程程序中仅初始化一次。这些互斥锁导致对库函数的调用。在调用函数之前,编译器必须确保在调用过程中保留调用者保存的,未使用的参数寄存器和被调用者保存的寄存器(它将不使用)。为此,编译器可以将寄存器值压入堆栈。
因此,即使静态变量初始化在程序流中仅发生一次,其简单存在也可以极大地更改生成的代码。