为什么递归深度是不确定的(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后,重复执行描述的程序,系统段错误前的递归次数不变。
重复运行以下 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
为什么 C++ 的最大递归数不同?
您的递归在逻辑上永远不会终止。它仅在您的程序因缺少堆栈而崩溃时终止 space.
每个递归调用都会使用一定数量的堆栈 space,但在 C++ 中,并没有准确定义多少堆栈 space 可用以及每次递归调用使用多少堆栈。
每次调用使用的堆栈 space 可能因优化设置、链接器选项、对齐要求、程序的启动方式以及大量其他因素而异。
底线:您编写了一个错误,并且您 运行 与编译器和平台中的未定义行为发生冲突。如果您想准确计算出您的程序在其当前线程上有多少堆栈 space,您的平台将提供您可以调用的 API 以获取该值。
炸毁堆栈时发生的事情并不能保证崩溃。根据系统的不同,您可能只是在相对随机的内存中浪费内存 space。
该内存中的内容可能取决于发生的内存分配、当您请求一些内存时 OS 交给您的连续内存量、ASLR 或其他。
C++ 中的未定义行为是不可预测的。
超越 C++ 方面:根据 Eljay 和 n.'pronouns'.m 的评论,我转向了 ASLR。 This post 描述了如何做到这一点。简而言之,可以通过
禁用 ASLRecho 0 | sudo tee /proc/sys/kernel/randomize_va_space
并通过
启用echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
禁用ASLR后,重复执行描述的程序,系统段错误前的递归次数不变。