C++函数调用与堆栈上压入/弹出的新块

发布于 2024-11-06 19:11:45 字数 487 浏览 1 评论 0原文

我正在阅读有关 C++ 中的变量作用域的内容,并遇到了一个有趣的块结构:

int main(int argc, char **argv) {
    int local;

    { // New level of scope
        int more_local;
    }

    return 0;
}

我知道变量在由右大括号 } 表示的每个块的末尾从堆栈中弹出。

我还读到,函数调用还将它们的变量压入堆栈,并在调用结束时终止,用大括号 } 表示:

void foo() {
    int more_local;
}

int main(int argc, char **argv) {
    int local;
    foo();

    return 0;
}

在这两种情况下堆栈的处理方式有何不同,以及两者的优点和缺点?

I was reading about variable scope in C++ and encountered an interesting block structure:

int main(int argc, char **argv) {
    int local;

    { // New level of scope
        int more_local;
    }

    return 0;
}

I understand that variables are popped out of the stack at the end of each block denoted by the closing curly brace }.

I've also read that function calls also push their variables on the stack and terminate at the end of the call denoted by closing curly brace }:

void foo() {
    int more_local;
}

int main(int argc, char **argv) {
    int local;
    foo();

    return 0;
}

How is the stack handled differently in both situations and what are the advantages and disadvantage of both?

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

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

发布评论

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

评论(5

旧时光的容颜 2024-11-13 19:11:45

通过函数调用,您将返回地址压入堆栈并创建一个新的堆栈帧。如果您只是将部分代码括在花括号中,那么您就是在定义一个新范围,正如您所说。它们就像任何跟随控制语句(例如 if、for、while 等)的代码块一样。

这里不能真正谈论优点和缺点,因为这是两个完全不同的东西。在很多情况下,将代码块括在大括号中会受益,并且它会使代码更难以阅读。

With the function call, you are pushing the return address onto the stack and creating a new stack frame. If you just enclose parts of code in curly braces, you are defining a new scope, as you said. They are just like any block of code following a control statement such as if, for, while, etc.

You can't really talk about advantages and disadvantages here, because these are two completely different things. There aren't very many situations where you will benefit from enclosing blocks of code in curly braces, and it can make code harder to read.

凹づ凸ル 2024-11-13 19:11:45

好吧,您可以说您的第一个示例可以被视为内联函数。 :P
但一般来说,函数调用和打开新的作用域彼此无关。
当您调用函数时,返回地址和所有参数都会被压入堆栈,并在函数返回后从堆栈中弹出。
当打开一个新的范围时,您只需在该范围的末尾调用该范围内所有对象的析构函数即可;决不能保证这些变量占用的实际空间立即从堆栈中弹出。可以,但该空间也可以简单地由函数中的其他变量重用,具体取决于编译器/优化器的想法。

Well, you could say that your first example could be seen as an inlined function. :P
But generally, function calls and opening a new scope have nothing to do with each other.
When you call a function, the return address and all arguments are pushed on the stacked and popped from it after the function returns.
When opening a new scope, you simple call the destructor of all objects within that scope at the end of it; it is by no means guaranteed that the actual space occupied by those variables is popped from the stack right away. It could, but the space could also simply be reused by other variables in the function, depending on the compilers / optimizers whims.

狼性发作 2024-11-13 19:11:45

int more_local; 在这两种情况下都将被放置在堆栈上。但第二种情况会有函数调用的开销。

我建议您考虑一下:

void foo()
{
    int local;

    { // New level of scope
        int more_local_1;
    }
    { // New level of scope
        int more_local_2;
    }
}

这里 more_local_1more_local_2 可能共享相同的内存位置。一旦它用于
more_local_1more_local_2 变量的第二个作用域。

int more_local; will be placed on the stack in both cases. But second scenario will have an overhead of function call.

I would suggest you to think about rather this:

void foo()
{
    int local;

    { // New level of scope
        int more_local_1;
    }
    { // New level of scope
        int more_local_2;
    }
}

Here more_local_1 and more_local_2 may share the same memory location. Once it used for
more_local_1 and in second scope for more_local_2 variable.

屋顶上的小猫咪 2024-11-13 19:11:45
  • 局部作用域仍然可以访问其他局部变量,而函数必须显式传递它们需要使用的任何调用者变量

    • 传递变量是一件痛苦的事情,但有时它使代码更容易理解,以清楚地指示作用域操作实际所需的较小变量集(以及鼓励将操作分组为离散的功能单元,给定适当的函数名称和上下文相关的参数名称,然后它们就可以重用了)
  • out-of-line函数调用还有一些其他堆栈空间&性能开销:返回地址、保存的寄存器、调用和返回指令

  • 与函数作用域相比,局部作用域特别适合最小化保存重要资源(例如大量内存、线程、文件描述符和/或)的变量的作用域。锁:函数的级别越高、运行时间越长,及时清理它就越有用

    • 缩短变量生命周期还减少了程序员必须在心里“跟踪”以理解和维护代码的并发变量数量:越少越好
  • 有时它并没有多大作用当您执行一组类似的操作时,必须选择任意不同的标识符是有意义的,因此某些本地作用域允许方便地“回收”标识符

  • 本地作用域有点笨拙,会在源代码中占用“屏幕空间”,并增加缩进级别,因此最好在有特定理由时使用它们,而不是“只要可以”就使用它们

  • local scopes can still access the other local variables, while functions have to be explicitly passed any of the caller's variables they need to use

    • passing variables is a pain, but sometimes it makes code more understandable to clearly indicate the smaller set of variables actually needed for the scoped operations (as well as encouraging operations to be grouped into discrete functional units, given an appropriate function name and context-relevant parameter names, and they're then ready for reuse)
  • out-of-line function calls have a few other stack space & performance overheads: return addresses, saved registers, call and return instructions

  • compared to function scope, local scopes are particularly good for minimising the scope of variables that hold important resources such as larger amounts of memory, threads, file descriptors and/or locks: the higher-level and longer-running the function the more useful it might be to clean it up promptly

    • reduced variable lifetime also reduces the number of concurrent variables the programmer has to mentally "track" to comprehend and maintain the code: the less the better
  • sometimes it doesn't make much sense to have to choose arbitrary different identifiers when you're doing a set of similar operations, so some local scopes allow the identifier to be "recycled" conveniently

  • local scopes are a bit clumsy and take "screen space" in source code, as well as increasing indentation level, so it's a good idea to use them when there's a specific justification rather than on a "whenever you can" basis

灼疼热情 2024-11-13 19:11:45

如果您观察这两个程序的汇编代码,似乎没有什么区别,因为编译器似乎会生成汇编代码,以在遇到左花括号或函数调用时在当前堆栈指针处推送新的堆栈帧并将该帧弹出一旦遇到右花括号或 return 语句。第二种情况的优点是您可以使用 return 语句将值返回给调用者函数。但不是第一种情况。

If you observe the assembly code for both the programs it seems that there is no difference because compiler seems to generate assembly code to push a new stack frame at current stack pointer when it encounters a opening curly brace or a function call and pop the frame out once it encounters closing curly brace or return statement.The advantage in second case is that you can return a value with return statement to caller function. But not in first case.

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