30

变量的作用域和生命周期之间有什么关系?如果一个变量超出范围,它的内存是否允许被另一个变量覆盖,或者是保留的空间,直到函数离开。

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

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


}
4

6 回答 6

35

什么是范围?

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

什么是一生?

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

因为,自动/局部非静态变量Lifetime仅限于它们的Scope.
换句话说,一旦创建它们的作用域( {, )结束,自动变量就会自动销毁。}因此名称自动开始。

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

所以是的,你的代码有一个未定义的行为

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

请注意,技术上x不存在超出其范围的情况,但可能会发生编译器没有删除的内容,x并且可以x通过指针访问超出其范围的内容(如您所做的那样)。但是,执行此操作的代码不是有效的 C++ 代码。它是调用未定义行为的代码。这意味着任何事情都可能发生(您甚至可能会看到x完好无损的价值),人们不应该期望从这样的代码中观察到可观察的行为。

于 2012-06-21T11:51:05.347 回答
9

C11 第 6.2.1 节第 2 段

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

.

C11 第 6.2.4 节第 2 段

对象的生命周期是程序执行期间保证为其保留存储的部分。

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

另一方面,例如,一个static局部变量,在这种情况下,作用域是块的本地变量,因此我们不能在块之外通过它的名称访问,但生命周期是整个程序,所以我们可以使用程序运行时变量在程序中任何位置的地址。这个例子应该有助于获得差异。

于 2012-06-21T11:58:57.810 回答
7

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

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

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

于 2012-06-21T11:53:49.263 回答
2

变量的作用域和生命周期之间有什么关系?

正如 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变量在创建它的范围关闭时结束它的生命周期,因此您有未定义的行为。

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

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

于 2012-06-21T12:04:25.783 回答
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.
于 2012-06-21T12:40:19.333 回答
0

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

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

于 2012-06-21T12:00:54.427 回答