为什么无限递归会导致seg错误? 为什么堆栈溢出导致seg错误。 我正在寻找详细的解释。
int f()
{
f();
}
int main()
{
f();
}
答案 0 :(得分:16)
每次调用f()时,都会增加堆栈的大小 - 这就是存储返回地址的位置,因此程序知道f()完成时的去向。由于您永远不会退出f(),因此每次调用堆栈将至少增加一个返回地址。一旦堆栈段满了,就会出现段错误。每个操作系统都会得到类似的结果。
答案 1 :(得分:14)
Segmentation fault是一个条件。无限递归会导致堆栈增长。并且成长。并且成长。最终,当它溢出到内存区域时,操作系统将禁止您访问程序。那是你得到分段错误的时候。
答案 2 :(得分:4)
您的系统资源有限。他们是有限的。即使你的系统拥有整个地球上最大的存储空间和存储空间,也无比你所拥有的更大。现在请记住。
“无限次”做事的唯一方法是“忘记”以前的信息。也就是说,你必须“忘记”以前做过的事情。否则你必须记住以前发生的事情并且需要存储一种或另一种形式(缓存,内存,磁盘空间,在纸上写下来......) - 这是不可避免的。如果您要存储东西,则可用空间有限。回想一下,这种无限比你拥有的更大。如果您尝试存储无限量的信息,则会耗尽存储空间。
当您使用递归时,您将隐式地存储每个递归调用的先前信息。因此,如果您尝试无限次地执行此操作,在某些时候您将耗尽您的存储空间。在这种情况下,您的存储空间是堆栈。堆栈是一段有限的内存。当你全部使用它并试图访问超出你拥有的内容时,系统将产生一个异常,如果它试图访问的内存被写保护,最终可能导致seg错误。如果它没有写保护,它将继续运行,覆盖上帝知道什么,直到它试图写入不存在的内存,或者它试图写入其他一些写保护的内存,或直到它破坏你的代码(在内存中)。
答案 3 :(得分:3)
它仍然是一个stackoverflow; - )
问题是C运行时不像其他托管语言那样提供“工具化”(例如Java,Python等),因此在为堆栈指定的空间之外写入而不是引起详细的异常只会引发较低级别的错误,其通用名称为“分段错误”。
这是出于性能原因,因为这些内存访问监视程序可以在硬件支持的帮助下设置,只需很少或没有开销;我现在不记得确切的细节,但通常是通过标记MMU页表或大部分过时的段偏移寄存器来完成的。
答案 4 :(得分:2)
AFAIK:堆栈的末尾受到进程无法访问的地址的保护。这可以防止堆栈在分配的数据结构上增长,并且比显式检查堆栈大小更有效,因为无论如何都必须检查内存保护。
答案 5 :(得分:1)
程序copunter或指令指针是包含要执行的下一条指令的值的寄存器。 在函数调用中,程序计数器的当前值被压入堆栈,然后程序计数器指向函数的第一条指令。从该函数返回并分配给程序计数器后,旧值被激活。在无限递归中,值被反复推送并导致堆栈溢出。
答案 6 :(得分:0)
它与缓冲区溢出的原理基本相同;操作系统为堆栈分配固定数量的内存,当你用完(堆栈溢出)时,你会得到未定义的行为,在这种情况下,这意味着一个SIGSEGV。
基本理念:
int stack[A_LOT];
int rsp=0;
void call(Func_p fn)
{
stack[rsp++] = rip;
rip = fn;
}
void retn()
{
rip = stack[--rsp];
}
/*recurse*/
for(;;){call(somefunc);}
最终rsp移动到堆栈的末尾,并尝试将下一个返回地址放在未分配的存储和程序barfs中。显然,真实的系统比这更复杂,但是这可能(并且已经)占用了几本大书。
答案 7 :(得分:0)
在“低”级别,堆栈通过指针(堆栈指针)“保持”,保存在处理器寄存器中。该寄存器指向内存,因为堆栈毕竟是内存。当您在堆栈上按下值时,其“值”会递减(堆栈指针从较高地址移动到较低地址)。每次进入函数时,都会从堆栈中“占用”一些空间(局部变量);此外,在许多体系结构中,对子例程的调用会推送堆栈上的返回值(如果处理器没有特殊的寄存器堆栈指针,则可能会使用“普通”寄存器,因为即使子例程可以使用,堆栈也很有用用其他机制调用),这样堆栈至少可以通过指针的大小(比如4或8个字节)来缩小。
在无限递归循环中,在最好的情况下,只有返回值会导致堆栈递减...直到它指向程序无法访问的内存。你会看到分段故障问题。
您可能会发现有趣的this page。