范围与变量的生命周期

时间:2012-06-21 11:50:15

标签: c++ scope

变量的范围和生命周期之间的关系是什么? 如果一个变量超出了范围,它的内存是否允许被另一个变量覆盖,或者是该函数被保留之前保留的空间。

我很好,因为我想知道下面的代码是否真的有效,或者是否可能是* p未定义

foo() {
  int *p;
  {
    int x = 5; 
    p = &x;
  }
  int y = *p;


}

6 个答案:

答案 0 :(得分:27)

  

什么是范围?

范围 是可以访问变量的代码区域或部分。

  

什么是生命?

生命周期 是对象/变量处于有效状态的持续时间。

对于 自动/本地非静态变量Lifetime仅限于Scope
换句话说,一旦创建它们的范围({})结束,自动变量就会自动销毁。因此,名称自动开始。

  

您的代码示例有什么问题?

是的,您的代码有未定义的行为

在您的示例中,*p的范围是创建后的整个函数体 但是,x是一个非静态的本地/自动变量,因此x的生命周期以其范围结束,即一旦范围结束,创建它的}的右括号x不存在。 *p指向不再存在的东西。

请注意,技术上x不存在于其范围之外,但是编译器可能没有删除x的内容,并且可能会访问x以外的内容它的作用域通过指针(就像你做的那样)。但是,执行此操作的代码不是有效的C ++代码。它是一个调用Undefined Behavior的代码。这意味着任何事情都可能发生(你甚至可能看到x的值完整),并且人们不应该期望这样的代码可以观察到行为。

答案 1 :(得分:7)

对象(即存储值的实际底层事物)具有生命周期。

变量(即用于引用对象的东西)具有范围。

无论哪种方式,y = *p都会调用未定义的行为; x引用的对象是自动,其生命周期在x超出范围时结束。

答案 2 :(得分:7)

C11第6.2.1节第2段

  

对于标识符指定的每个不同实体,   标识符仅在区域内可见(即,可以使用)   程序文本称为其范围。由指定的不同实体   相同的标识符具有不同的范围,或者具有不同的名称   空间

C11第6.2.4节第2段

  

对象的生命周期是程序执行期间的一部分   保证保留哪个存储空间。

在你的情况下,x是块的本地,因此它的生命周期也是如此,因此x不能通过块外的名称来访问,也因为它的生命周期仅限于其块的地址<{1}}在离开块后不再被保留,因此会导致未定义的行为。

另一方面,例如,假设一个x局部变量,在这种情况下,范围是块的本地,因此我们不能通过块名外的名称访问,但是生命周期是整个程序,所以我们可以在程序运行时的任何地方使用变量的地址。这个例子应该有助于发挥作用。

答案 3 :(得分:1)

  

变量的范围和生命周期之间的关系是什么?

正如Oli所说,变量具有范围和对象的生命周期。变量的范围和它的生命周期是绑定的,但是您可以拥有其生命周期超出创建范围的对象:

int* f() {
   int *p                    // Variable p
          = new int(1);      // object (call it x)
   return p;                 // p scope ends, p lifetime ends, x survives
}
int main() {
   int *q                    // Variable q
          = f();             // ... points to x
   delete q;                 // x lifetime ends, q is in scope, q is alive
}

在您的特定情况下,x变量在创建它的作用域关闭时结束它的生命周期,因此您有未定义的行为。

  

如果一个变量超出范围,它的内存是否允许被另一个变量覆盖,或者是该函数被保留之前保留的空间。

这是一个实施细节。在所有情况下访问变量都是未定义的行为并且因为并非所有情况都必须相等,并且如果变量具有非平凡的析构函数,则会在作用域的末尾调用它,因此是否内存是否存在无关紧要:对象不再存在。话虽这么说,在很多情况下,当变量超出范围时,编译器不会释放函数中的内存(即它们不会重置帧指针),但是它们可能会重用相同的空间来保存同一函数中的其他变量。

答案 4 :(得分:1)

我已经运行了你的程序,输出是5.输出仍然是5虽然x变量在第二个'}'之后被销毁,因为内存位置没有被任何其他变量覆盖。如果你有很多在第二个范围结束之后的代码,“x”以前拥有的内存位置很可能被覆盖。

int x = 5; 
*p = &x;
}  //  x lifetime ends after this.but the number '5' is still there in that memory.
   //but it is possible to assign this memory (which previously belong to x) to a new var.

答案 5 :(得分:0)

标准定义了在保留范围后允许覆盖内存。 编译器实现使其保留和释放。编译时优化可能会移动堆栈分配,因此它们将不再反映您的订单。

您的代码确实会调用未定义的行为,因此不应使用。 您的代码实际上可能有效,因为在将值p指向后,写入y的值会发生。