1

我正在学习函数并决定创建一个循环,其中两个函数(在本例中为funcAfuncB)永远互相调用,但它会在一段时间后停止执行。代码如下所示:

#include <iostream>

void funcA(); //forward declaration

//funcB calls funcA 
void funcB()
{
    funcA();
}

//funcA prints 1 and calls funcB again 
void funcA()
{
    std::cout<<1;
    funcB();
}

//main calls funcB
int main()
{
    funcB();
    return 0;
}  

返回值为-1073741571( 0xC00000FD)。你能解释为什么会这样吗?

4

3 回答 3

4

虽然理论上这会永远持续下去,但问题是每次调用函数时都需要在称为“堆栈”的东西上保留一些内存。最终,那个“堆栈”上没有更多空间了,你会得到一个 StackOverflowException。0xC00000FD是 windows 用来告诉您发生 StackOverflowException 的错误代码

于 2019-07-31T16:10:36.303 回答
4

每当您调用函数时,程序都会保留少量空间来保存该函数的信息(用于局部变量的空间、有关函数退出后返回到何处的信息等)。该内存是从称为调用堆栈(或简称“堆栈”)的区域分配的,并在函数完成运行时返回到堆栈中。

堆栈通常具有固定的小尺寸。如果您有很长的调用链(通常以数万次调用衡量),您可能会用完堆栈空间,并且程序将终止并出现称为堆栈溢出的错误。这就是你的程序中发生的事情。

一般来说,如果你有递归(或者在你的情况下,相互递归)函数,你需要确保递归深度不会因为某些“太大”的定义而变得“太大”,或者这种事情可能发生。在某些特殊情况下,某些编译器可以识别出您正在编写递归代码并将其转换为不分配多个堆栈帧的代码(有关详细信息,请参阅“尾调用消除”),但这是例外而不是规则。

于 2019-07-31T16:12:04.160 回答
4

好的,因为这是 Windows 10 上的 gcc,所以看看这个goodbolt

在没有启用任何优化的情况下,这两个函数都被显式调用。

b():
        push    rbp
        mov     rbp, rsp
        call    foo()
        call    a()
        nop
        pop     rbp
        ret
a():
        push    rbp
        mov     rbp, rsp
        call    b()
        nop
        pop     rbp
        ret

正如其他答案指出的那样,每个调用都会在堆栈信息中留下如何返回到调用函数的函数。现在,由于函数永远不会返回,因此这些信息永远不会从堆栈中删除,而是不断添加。结果,您遇到了堆栈溢出,在 Windows 上由 value 指示0xC00000FD

现在,如果您启用优化 ( -O2),编译器能够确定这是不定式循环(使用称为尾递归的技术)。

b():
        sub     rsp, 8
.L2:
        call    foo()
        jmp     .L2
a():
        sub     rsp, 8
.L6:
        call    foo()
        jmp     .L6

因此,启用优化后,您将拥有预期的不定式循环。

于 2019-07-31T16:43:24.473 回答