C 堆栈内存与算术和递归函数调用一起使用

C 堆栈内存与算术和递归函数调用一起使用,c,memory,recursion,C,Memory,Recursion,我关心的是在涉及算术和递归函数调用的指令中使用堆栈内存的问题,例如: return 5 + f(a-1); //f is recursive 我想了解堆栈上放了什么,什么时候放,这样我就知道在深度递归的情况下,什么可能或不可能导致内存问题。下面是一个更完整的示例: int f(int a) { if (a > 0) { return 5 + f(a-1); } else { return 0; } } 让我们关注一下返回5

我关心的是在涉及算术和递归函数调用的指令中使用堆栈内存的问题,例如:

return 5 + f(a-1); //f is recursive
我想了解堆栈上放了什么,什么时候放,这样我就知道在深度递归的情况下,什么可能或不可能导致内存问题。下面是一个更完整的示例:

int f(int a) {
    if (a > 0) {
        return 5 + f(a-1);
    }
    else {
        return 0;
    }
}
让我们关注一下
返回5+f(a-1)的行在该点附近,我们必须在内存中存储什么

  • 因为变量是f的局部变量,所以整数a在堆栈中有一个位置
  • 是否会存储值5和1
  • a-1操作的结果如何,它会被放在堆栈上吗
  • f的返回值是多少
关于内存使用量的“最坏情况”是,在某个时候,所有这些都将同时在堆栈上。更好的情况是按顺序分配和取消分配,这样并非所有内容都保存在内存中

它将如何发生?这取决于编译器吗


非常感谢,

据我所知,将发生以下情况:

  • a
    必须放在堆栈上,因为它是一个局部变量
  • 当然,
    f
    的参数必须放在堆栈上
  • 值5应该放在堆栈上,因为它需要保存以供以后的
    +
    操作使用
  • 如果我没记错的话,返回值被视为一个参数,因此它也将被放在堆栈上
  • 当然还有指令指针
但是可能编译器进行了一些我不知道的优化。

如果它保持递归,那么堆栈上必须至少有一个堆栈帧的空间(例如,以前的堆栈指针或用于维护堆栈帧的等效寄存器、返回地址和返回值的空间)以及传入的
a-1
变量。
5
1
几乎肯定不会出现在堆栈上,它们很可能是在代码中硬编码的

这看起来可能不多,但如果你调用
f(999999999)
,你将杀死你的堆栈。递归并不适合
a-1
类型的操作

但是,您的编译器可能足够聪明,可以对如下内容进行后端递归优化:

int f(int a) {
    if (a <= 0) return 0;
    return 5 + f(a-1);
}

:-)

看看这个关于

此外,如果您担心堆栈溢出,而编译器无法进行优化,则可以通过将tail转换为while循环将代码转换为非递归的替代方案,或者对于非tail递归函数,您应该能够自己创建和管理堆栈数据结构(请参阅)

上帝保佑

此上下文中的“堆栈”也可以是内部CPU寄存器/缓存,具体取决于CPU和编译器。我会把它们都称为堆栈,以保持简单

每个函数调用存储在堆栈上的内容包括: -变量a。 -返回的值。 -呼叫者的返回地址。 -可能是“条件代码寄存器”或等效的基本CPU寄存器,具体取决于体系结构。可能还有程序计数器等。也就是说,每次调用函数时所得到的静态开销

表情

返回5+f(a-1)

5是一个常数,将存储在程序内存中,不会影响堆栈。-1极有可能被转换为汇编指令“减少1”,因此也会在程序内存中结束

表达式a-1的结果将存储在堆栈上,结果将成为下一次递归的“新a”


总而言之,这种情况下的罪魁祸首不是在堆栈上来回显示的变量,而是函数调用开销本身所需的堆栈空间。

没有理由在堆栈上有
5
。“a”是f()的参数。5不在堆栈上。什么是指令指针?@Lundin,f()的参数是-1。。。至少在理论上(优化之前)不应该单独保存吗?请参阅下面我的帖子。它将被单独保存,尽管每个函数调用只堆叠一次,而不是像这篇文章所建议的那样堆叠两次。如果投票人愿意指出为什么这个答案没有用,我可以考虑修复它。但我很确定它自己没有问题,“如果它保持递归”是否意味着“如果编译器没有以非递归的方式优化代码”?(是的,这只是一个示例,只是为了说明和简单)你能简单地说明一下sp/bp代表什么吗?是的,gcc(例如)有一些优化,让我花了好几天的时间试图弄清楚它们。我的意思是“如果编译器让它(未优化)为递归”。sp/bp是堆栈指针和基指针,两个x86寄存器。堆栈帧通常是通过保存堆栈上的最后一个堆栈指针来构建的,因此可以轻松恢复最后一个帧。显然,这是特定于体系结构的。“sp”是堆栈指针的通用缩写,我见过的每种CPU都使用它。至于“bp”,因为原始海报从未提到使用x86,所以我假设它的意思是英国石油公司。
int f(int a) { return (a > 0) ? a * 5 : 0; }