【问题标题】:Within the same function, is it UB to access—through indirection—a local variable not in scope?在同一个函数中,UB 是否可以通过间接访问一个不在作用域内的局部变量?
【发布时间】:2012-12-16 11:38:15
【问题描述】:

在第二个右大括号之后,b 只能通过 a 间接访问。

int main() {
    int *a;
    {
        int b = 42;
        a = &b;
    }
    printf("%d", *a); // UB?
    return 0;
}

由于b 不再在范围内,这是 UB 吗?我知道从已经返回的函数中取消引用指向非静态局部变量的指针是 UB,但在这种情况下,一切都在同一个函数中。

这是 C++ 中的 UB,但我不确定 C。

【问题讨论】:

  • 如果您在 printf 之前声明 int c = 255;,我预测您将得到 255 而不是 42。这应该表明 int b 的堆栈区域已被丢弃并重用。跨度>
  • @ZanLynx with clang 3.2 无论是否启用优化,它仍然打印 42。

标签: c scope undefined-behavior


【解决方案1】:

是的,访问 任何 已达到其生命周期的变量是未定义的行为。 范围和存储持续时间在这里略有不同。范围更多的是“变量标识符何时可见?”并且存储持续时间是“变量本身何时存在?”。

你可以拥有范围和持久的东西,例如:

int main (void) {
    int spoon = 42;
    // spoon is both in scope and enduring here
    return 0;
}

或超出范围但持久:

int main (void) {
    int *pSpoon;
    {
        static int spoon = 42;
        pSpoon = &spoon;
    }
    // spoon is out of scope but enduring here (use *pSpoon to get to it)
    return 0;
}

你也可以让变量超出范围并且不持久,例如:

int main (void) {
    // spoon is neither in scope nor enduring here ("there is no spoon")
    return 0;
}

事实上,你唯一不能拥有的是范围内的变量,但不是持久的。标识符与存储相关联,因为允许变量没有后备存储几乎没有意义。

我在这里不是在谈论指针,这是一个额外的间接级别 - 范围内的指针变量 总是 存储指针值本身,即使它指向的东西 to 已结束或尚未开始其存储期限。

未定义的行为在某些情况下可能起作用这一事实绝不会使行为已定义,事实上,这是未定义行为最烦人的特性之一,因为它有时确实起作用。否则会更容易被发现。

在这种特殊情况下,b 变量存储持续时间在内部右大括号处结束,因此在该点之后尝试访问它是不明智的。


标准的控制部分是c11 6.2.4 Storage duration of objects(稍微改写以删除不必要的部分):

对象具有决定其生命周期的存储持续时间。有四个存储 持续时间:静态、线程、自动和分配。

其标识符声明为没有链接且没有存储类说明符 static 的对象具有自动存储持续时间。

对于这样的对象,它的生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。

【讨论】:

  • 行为被定义为未定义。
  • @Stacked: 不,行为被 declared 定义为 :-) C 程序员应该理解声明/定义的区别。
  • 您引用的第 6.2.1 节的部分仅提及标识符的范围,而不是它们可能代表的对象。
  • 由于printf的参数是*a,我认为6.2.4(2)“当指针指向(或刚刚过去)的对象到达末尾时,指针的值变得不确定它的一生。”更贴切。 (加6.5.3.2(4),Fn 102:“……在无效值中……以及对象生命周期结束后的地址。”)
  • @DanielFischer b 的生命周期直到右括号,对吧?
【解决方案2】:

b 仍然在另一个嵌套的范围内,当您访问它时,该范围已退出并因此销毁 b。所以它是未定义的行为。同一功能内的一切都是无关紧要的。

出于变量生命周期的目的,您可以将“已返回的函数”视为嵌套范围,反之亦然。

【讨论】:

  • 只有一个挑剔(这也在我的原始答案中)。它的范围已经结束,但这不是问题所在。这仅仅意味着您不能使用b 来访问它,因为b 不可见。问题是 storage duration 也已结束,这意味着您也无法使用 anything(例如*a)来获取它。
  • @paxdiablo,这可能是两个独立的概念(变量的生命周期和变量引用的对象的生命周期),但是在像 C++ 这样的语言中,它本身只支持值类型,我会说它们非常紧密地交织在一起,只要变量是在堆栈上定义的,而不是指向堆分配对象的指针
  • @KarthikT 问题是关于 C,而不是 C++。
  • @Zoidberg'-- 是的,现在编辑为时已晚.. 无论如何,这一点也适用于 C。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-25
相关资源
最近更新 更多