1

我正在阅读 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;
}

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

4

5 回答 5

4

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

你不能在这里真正谈论优点和缺点,因为这是完全不同的两件事。用花括号括起来的代码块不会让您受益的情况并不多,而且它会使代码更难阅读。

于 2011-05-10T00:57:37.933 回答
2

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

于 2011-05-10T00:50:34.803 回答
2

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_1并在第二个范围内。more_local_2

于 2011-05-10T01:01:54.753 回答
1
  • 局部作用域仍然可以访问其他局部变量,而函数必须显式传递它们需要使用的任何调用者变量

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

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

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

  • 本地范围有点笨拙,在源代码中占用“屏幕空间”,以及增加缩进级别,因此在有特定理由而不是“尽可能”的基础上使用它们是一个好主意

于 2011-05-10T01:15:32.893 回答
0

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

于 2015-06-14T16:46:23.917 回答