为什么递归深度是不确定的(C++)?

Why is the recursion depth non-deterministic (C++)?

重复运行以下 C++ 程序会在出现分段错误之前给出不同的最大递归调用次数(相差约 100 次函数调用)。

#include <iostream>

void recursion(int i)
{
    std::cout << "iteration: " << ++i  << std::endl;
    recursion(i);
}

int main()
{
    recursion(0);
};

我用

编译了文件main.cpp
g++ -O0 main.cpp -o main

Here and java 讨论了与上述相同的问题。在这两种情况下,答案都基于 java 相关概念,JIT、垃圾收集、HotSpot 优化器等

为什么 C++ 的最大递归数不同?

您的递归在逻辑上永远不会终止。它仅在您的程序因缺少堆栈而崩溃时终止 space.

每个递归调用都会使用一定数量的堆栈 space,但在 C++ 中,并没有准确定义多少堆栈 space 可用以及每次递归调用使用多少堆栈。

每次调用使用的堆栈 space 可能因优化设置、链接器选项、对齐要求、程序的启动方式以及大量其他因素而异。

底线:您编写了一个错误,并且您 运行 与编译器和平台中的未定义行为发生冲突。如果您想准确计算出您的程序在其当前线程上有多少堆栈 space,您的平台将提供您可以调用的 API 以获取该值。

炸毁堆栈时发生的事情并不能保证崩溃。根据系统的不同,您可能只是在相对随机的内存中浪费内存 space。

该内存中的内容可能取决于发生的内存分配、当您请求一些内存时 OS 交给您的连续内存量、ASLR 或其他。

C++ 中的未定义行为是不可预测的。

超越 C++ 方面:根据 Eljay 和 n.'pronouns'.m 的评论,我转向了 ASLR。 This post 描述了如何做到这一点。简而言之,可以通过

禁用 ASLR
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

并通过

启用
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

禁用ASLR后,重复执行描述的程序,系统段错误前的递归次数不变。