我在做一些有关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");
}
答案 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
)编译代码,则编译器将使用尾部调用优化,并且您的代码不会出现堆栈溢出。