算术和递归函数调用中堆栈内存的使用

发布于 2024-10-14 17:01:30 字数 615 浏览 0 评论 0原文

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

return 5 + f(a-1); //f is recursive

我想了解什么时候放在堆栈上,以便我知道什么可能或不能导致内存深度递归的情况下的问题。这是一个更完整的示例:

int f(int a) {
    if (a > 0) {
        return 5 + f(a-1);
    }
    else {
        return 0;
    }
}

让我们关注一下 return 5 + f(a-1); 行,在该点附近我们必须在内存中存储什么?

  • 我们在堆栈中确实有一个位置用于存放整数 a,因为该变量是 f 的局部变量,
  • 值 5 和 1 会被存储吗?
  • 那么a-1的运算结果呢,会被放入栈中吗?
  • f的返回值呢?

关于内存使用量的“最坏情况”场景是,在某个时刻所有这些都将同时位于堆栈上。更好的情况是按顺序进行分配和释放,这样并不是所有内容都保存在内存中。

会怎样发生呢?是编译器决定的吗?

非常感谢,

My concern is about what will use stack memory in an instruction involving arithmetics and a recursive function call such as:

return 5 + f(a-1); //f is recursive

I'd like to understand what is put on the stack, and when, so that I know what could or couldn't cause memory issues in case of a deep recursion. Here is a more complete example:

int f(int a) {
    if (a > 0) {
        return 5 + f(a-1);
    }
    else {
        return 0;
    }
}

Let's focus on the line return 5 + f(a-1); What will we have to store in memory around that point?

  • We do have a place in the stack for the integer a since the variable is local to f
  • will the values 5 and 1 be stored?
  • what about the result of the operation a-1, will it be put on the stack?
  • what about the return value of f?

The "worst case" scenario regarding the amount of memory used would be that at some point all of these will be on the stack at the same time. A better case would be that there will be allocations and deallocations in sequence such that not everything is kept in memory.

How will it happen? Is it up to the compiler?

Many thanks,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

淡淡離愁欲言轉身 2024-10-21 17:01:30

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

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

但是,您的编译器可能足够聪明,可以对类似这样的事情进行尾端递归优化:

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

当然,我假设您的代码只是一个示例,因为我认为它可能会被非递归甚至替换非迭代:

int f(int a) { return (a > 0) ? a * 5 : 0; }

:-)

If it stays recursive, you'll have to have space on the stack for at least a stack frame (eg, previous stack pointer or equivalent register for maintaining stack frames, return address and space for return value) and the a-1 variable being passed in. The 5 and the 1 almost certainly wouldn't go on the stack, they'd be hard-coded within the code most likely.

That may not seem like much but, if you call f(999999999), you're going to kill your stack. Recursion's not that suited for a-1-type operations.

However, your compiler may be smart enough to do tail-end recursion optimisation on something like this:

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

Of course, I'm assuming that that code you is just a sample since I think it could probably be replaced with the non-recursive and even non-iterative:

int f(int a) { return (a > 0) ? a * 5 : 0; }

:-)

看轻我的陪伴 2024-10-21 17:01:30

在这种情况下,“堆栈”也可以是内部 CPU 寄存器/高速缓存,具体取决于 CPU 和编译器。为了简单起见,我将它们全部称为堆栈。

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

表达式

返回 5 + f(a-1);

5 是一个常量,将存储在程序存储器中并且不影响堆栈。 -1很可能会被转换为汇编指令“减一”,因此最终也会进入程序存储器。

表达式a-1的结果将被存储在堆栈中,并且该结果将成为下一次递归的“新a”。

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

"Stack" in this context could as well be internal CPU registers / cache, depending on CPU and compiler. I'll call them all stack to keep things simple.

The things that are stored on the stack per function call are:
- The variable a.
- The returned value.
- The return address of the caller.
- Possible the "condition code register" or equivalent fundamental CPU register, depending on the architecture. Possibly the program counter etc as well. I.e the static overhead you get each time when calling a function.

The expression

return 5 + f(a-1);

The 5 is a constant that will be stored in program memory and doesn't affect the stack. The -1 will most likely be transformed into the assembler instruction "decrease by one", and thus end up in program memory as well.

The result of the expression a-1 will be stored on the stack and the result will become the "new a" for next recursion.

To sum this up, the big culprit in this case isn't really the variables you showel back and forth on the stack, but the stack space needed for the function call overhead itself.

贵在坚持 2024-10-21 17:01:30

Take a look at this site about C Function Call Conventions and the Stack

Also, if you're worring about stack overflows and your compiler is not able to optimize tail recursion, you can transform your code to a non-recursive alternative by transforming tail into a while loop, or in the case of non tail recursive functions you should be able to create and manage a stack data-structure by yourself (see Way to go from recursion to iteration)

God bless!

忱杏 2024-10-21 17:01:30

据我了解,将会发生以下情况:

  • a 必须放置在堆栈上,因为它是局部变量。
  • 当然,f 的参数必须放置在堆栈上。
  • 值 5 应该放在堆栈上,因为需要保存它以供后面的 + 操作使用。
  • 如果我没记错的话,返回值被视为参数,因此它也会被放置在堆栈上。
  • 当然还有指令指针。

但也许编译器做了一些我不知道的优化。

As far as I understand, this is what will happen:

  • a must be placed on the stack because it's a local variable.
  • Naturally the parameter to f must be placed on the stack.
  • The value 5 should be placed on the stack because it needs to be saved for the later + operation.
  • If I remember correctly, the return value is treated like a parameter, so it will be placed on the stack too.
  • And of course the instruction pointer.

But maybe the complier does some optimizations I'm not aware of.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文