在循环完成后,循环内的变量(while或for)是否已处理?

时间:2014-10-24 21:20:04

标签: c++ variables

在循环完成执行后,是否在while或for循环中创建的变量是从内存中释放/删除的?另外,在循环中创建临时变量是一种不好的编码习惯吗?

在这个例子中,它是创建100个X变量然后处理它们,还是它们在每次迭代时处理?谢谢。 例如:

int cc =0;
while(cc < 100){
    int X = 99; // <-- this variable
    cc++;    
}

8 个答案:

答案 0 :(得分:20)

范围生命周期是两回事。对于在没有static的块范围内定义的变量,它们或多或少地紧密相关,但它们仍然是截然不同的概念 - 如果你不这样做,你可以用脚射击自己保持他们的直接。

从问题中引用代码段:

int cc =0;
while(cc < 100){
    int X = 99; // <-- this variable
    cc++;    
}

X范围是程序文本的区域,其名称可见。它从它定义的点延伸到封闭块的末尾,封闭块由{}字符分隔。 (块恰好是while语句的一部分这一事实并不直接相关;它是定义范围的块本身。)

在块中,名称X引用int变量。在块外,名称X不可见。

X生存期是程序执行期间X逻辑上存在的时间。它在执行到达开始{定义之前)时开始,并在执行到达结束}时结束。如果块被执行100次,则创建X并且&#34;销毁&#34; 100次,并且有100个不相交的生命周期。

虽然名称 X仅在其范围内可见,但可以随时(直接或间接)访问名为X对象在其一生中。例如,如果我们将&X传递给函数,那么该函数可以读取和更新对象,即使该函数完全超出其范围。

您可以获取X的地址,并保存该地址,以便在其生命周期结束后使用 - 但这样做会导致未定义的行为。指向其生命周期已结束的对象的指针是 indeterminate ,并且任何取消引用它的尝试 - 甚至是引用指针的值 - 都有未定义的行为。

当对象到达其生命周期的末尾时,实际上不会发生任何事情。语言只需要对象在其生命周期中存在;除此之外,所有赌注都已关闭。分配用于保存对象的堆栈空间可能会被释放(这通常只是意味着移动堆栈指针),或者,作为优化,堆栈空间可能会保留分配并重新用于循环的下一次迭代。

  

另外,在循环中创建临时变量是不是很糟糕?

完全没有。只要您不在对象的生命周期结束时保存对象的地址,就没有任何问题。作为性能优化,分配和释放通常在进入和退出函数而不是块时完成。将变量限制在合理的范围内是非常良好的编码习惯。它通过限制代码不同部分之间不必要的交互,使代码更容易理解。如果在循环体内定义了X,那么循环之外的任何东西都不会影响它(如果你没有做过太狡猾的事情),这样就可以更容易地推断出代码。

更新:如果X属于具有非平凡构造函数和/或析构函数的类型,则实际上必须在进入和退出块时执行X的创建和销毁(除非编译器能够优化该代码)。对于int类型的对象,这不是问题。

答案 1 :(得分:6)

是的,变量被创建和销毁N次,除非编译器以某种方式对其进行优化(我相信它可以)。当你只有一个int时,这不是什么大问题。当你在循环中重新创建99次复杂对象时,它会变得更有问题。

一个小实际例子:

#include <iostream>
using namespace std;

class TestClass
{
public:
    TestClass();
    ~TestClass();
};
TestClass::TestClass()
{
    cout<<"Created."<<endl;
}

TestClass::~TestClass()
{
    cout<<"Destroyed"<<endl;
}

int main() {
    for (int i = 0; i < 5; ++i)
    {
        TestClass c;
    }
    return 0;
}

在此代码中TestClass c将重新创建5次。 IdeOne Demo

答案 2 :(得分:4)

是。范围{ }中定义的任何变量:

if (true) { // Forgive me father for this heresy :)
    int a = 0;
}
a = 1; // This will throw an error

一旦超出范围,就会自动解除分配。

在这种情况下也是如此:

for (int i = 0; i < 10; ++i) {
    // Do stuff
    ...
}
i = 1; // This will throw an error

i的范围仅限于for循环,即使它未在{ }对中声明。

答案 3 :(得分:2)

如前所述,事实上,不需要重复创建/销毁局部变量。

这是不必要的CPU时间浪费。

示例,使用“gcc -S -masm = intel -fverbose-asm”编译

int crazyloop (int n) {
  if (n > 0) {
      int i;
      for (i = 0; i < n; i++)
         ;
  }
  return n;
}

相应的装配清单是:

_Z9crazyloopi:
.LFB0:
    .cfi_startproc
    push    rbp #
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp    #,
    .cfi_def_cfa_register 6
    mov DWORD PTR [rbp-20], edi # n, n
    cmp DWORD PTR [rbp-20], 0   # n,
    jle .L2 #,
    mov DWORD PTR [rbp-4], 0    # i,
.L4:
    mov eax, DWORD PTR [rbp-4]  # tmp89, i
    cmp eax, DWORD PTR [rbp-20] # tmp89, n
    jge .L2 #,
    add DWORD PTR [rbp-4], 1    # i,
    jmp .L4 #
.L2:
    mov eax, DWORD PTR [rbp-20] # D.3136, n
    pop rbp #
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

有趣的部分在于注册RBP的参考。 设置后,它不会改变。变量“i”始终位于[rbp-4]。 (代码的变化,更多的变量等,给出了相同的结果=没有重复的分配/解除分配=对堆栈顶部位置的修改)。

这是最明智的做法:想想一个循环数万次的循环。

其他编译器会以不同方式执行此操作吗可能是的,但是,为什么它会这样做呢?

安全可能是一个问题?不可能;在这种情况下,程序员应该在变量消失之前简单地覆盖变量。

答案 4 :(得分:1)

如果你想在循环之后访问变量,你应该声明它在循环之后

while(...) {
    int i = 0;
    i++; // valid
}
i++; // invalid, i doesnt exist in this context
循环之外的

i

int i = 0;
while(...) {
    i++; // valid
}
i++; // valid

viariable的生命周期仅限于创建它的上下文{...} 我们在考虑一个对象时,在到达}

时会调用析构函数

答案 5 :(得分:0)

是的,它们在超出范围时会被销毁。请注意,这不是特定于循环中的变量。此规则适用于automatic storage duration的所有变量。

答案 6 :(得分:0)

任何变量在其范围内保持活跃状态​​。在特定变量甚至不退出的范围之外,忘记访问其值。

for(;;)
{
    int var;  // Scope of this variable is within this for loop only.
}
// Outside this for loop variable `var` doesn't exits.

答案 7 :(得分:0)

在里面声明的变量将有自己的范围。 当你知道你不会在范围之外使用那个变量时,你可以使用它。 那个编译器的工作。 :)