正在讨论的计划尝试使用sum-of-first-n-natural-numbers
计算recursion
。我知道这可以使用简单的公式n*(n+1)/2
来完成,但这里的想法是使用recursion
。
该计划如下:
#include <stdio.h>
unsigned long int add(unsigned long int n)
{
return (n == 0) ? 0 : n + add(n-1);
}
int main()
{
printf("result : %lu \n", add(1000000));
return 0;
}
该计划适用于n = 100,000
,但当n
的值增加到1,000,000
时,会产生Segmentation fault (core dumped)
以下摘自gdb
消息。
Program received signal SIGSEGV, Segmentation fault.
0x00000000004004cc in add (n=Cannot access memory at address 0x7fffff7feff8
) at k.c:4
我的问题:
recursion depth
中的C
是否有任何硬连线限制?或recursion depth
取决于可用的堆栈内存?
程序收到reSIGSEGV信号的可能原因是什么?
答案 0 :(得分:12)
通常,限制将是堆栈的大小。每次调用函数时,都会吃掉一定量的堆栈(通常取决于函数)。吃掉的数量是堆栈帧,并在函数返回时恢复。程序启动时,堆栈大小几乎几乎是固定的,可以是操作系统指定的(通常可在那里调整),甚至可以在程序中进行硬编码。
某些实现可能有一种技术,可以在运行时分配新的堆栈段。但总的来说,他们没有。
有些函数会以稍微不可预知的方式使用堆栈,例如当它们在那里分配可变长度数组时。
可以编译某些函数以便以保留堆栈空间的方式使用尾调用。有时您可以重写您的函数,以便所有调用(例如它自己)都是最后一次调用,并期望编译器对其进行优化。
每次调用函数确切需要多少堆栈空间并不容易,并且它将受制于编译器的优化级别。在你的情况下,一个廉价的方法是每次打电话时打印&n
; n
可能会在堆栈上(特别是因为程序需要获取其地址 - 否则它可能在寄存器中),并且它的连续位置之间的距离将指示堆栈帧的大小。 / p>
答案 1 :(得分:4)
C中的递归深度没有理论上的限制。唯一的限制是你的实现,通常是有限的堆栈空间。
(请注意,C标准实际上并不需要基于堆栈的实现。我不知道任何基于堆栈的实际实现,但要牢记这一点。)
SIGSEGV可能由任何数量的东西引起,但超过你的堆栈限制是一个相对常见的。解除引用错误的指针是另一个。
答案 2 :(得分:3)
C标准没有定义函数调用的最小支持深度。如果确实如此,那么无论如何都很难保证,它会在5.2.4 Environmental limits
部分的某处提到它。
答案 3 :(得分:3)
1)预计堆栈的消耗将减少并写为尾递归优化。
gcc -O3 prog.c
#include <stdio.h>
unsigned long long int add(unsigned long int n, unsigned long long int sum){
return (n == 0) ? sum : add(n-1, n+sum); //tail recursion form
}
int main(){
printf("result : %llu \n", add(1000000, 0));//OK
return 0;
}