无法理解为什么这种简单的递归有效并崩溃

时间:2019-05-30 22:18:03

标签: c++

我在做一些有关C ++的技巧问题,我在与此类似的代码上运行,然后对其进行了修改以查看会发生什么。

我不明白为什么此递归起初会起作用(它的打印值为2到4764),然后突然引发异常。

我也不明白为什么我可以说用void函数返回而实际上返回的不是“ return;”。

谁能解释这两个问题?

#include<iostream>
using namespace std;

void function(int& a){
    a++;
    cout << a << endl;
    return function(a);
}

void main() {

    int b = 2;
    function(b);

    system("pause>0");
}

2 个答案:

答案 0 :(得分:0)

这些注释已正确标识出您的无限递归导致堆栈溢出-对同一函数的每个新调用都占用更多RAM,直到您用完为程序分配的数量为止(默认C ++堆栈大小因环境,范围从旧系统上的10s kB到高端上的10+ MB)。注释正确地标识了您的无限递归导致堆栈溢出-为此目的分配的空间量(默认C ++堆栈大小变化很大)取决于环境,范围从旧系统上的10s kB到高端的10+ MB)。尽管函数本身在内存方面做得很少,但是堆栈框架(跟踪哪个函数调用哪个其他正在进行的函数以及使用什么参数)会占用很多空间。

虽然对某些数据结构很有用,但递归程序不必深入数千层,通常可以添加一个停止条件(在这种情况下,甚至检查a > some_limit)以标识它们已到达的位置。较深,需要停止向堆栈中添加更多内容(普通return;)。

在这种情况下,可以通过简单的for循环获得完全相同的输出,因此我想这些技巧问题纯属实验性。

答案 1 :(得分:0)

在x86-64平台(例如笔记本电脑或台式机)上,函数被称为以下两种方式之一:

  • 带有call汇编指令
  • 带有jmp汇编指令

有什么区别?call汇编指令后面还有其他指令:调用函数时,代码将返回到调用它的位置。为了跟踪其位置,该函数使用堆栈上的内存。如果递归函数使用call进行调用,则在递归时它将占用越来越多的堆栈,最终导致堆栈溢出。

另一方面,一条jmp指令只是告诉CPU跳转到存储其他功能的代码段。如果某个函数正在调用自身,则CPU只会jmp返回到该函数的顶部,并使用更新的参数重新开始。这称为尾调用优化,在很多常见情况下,由于堆栈不会增长,因此可以完全防止堆栈溢出。

如果您以更高的优化级别(例如GCC上的-O2)编译代码,则编译器将使用尾部调用优化,并且您的代码不会出现堆栈溢出。