3

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

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

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

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

4

2 回答 2

5

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

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

于 2013-01-02T02:56:18.443 回答
5

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

您可以拥有范围和持久的事物,例如:

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;
}

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

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

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

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


标准的控制部分是c11 6.2.4 Storage duration of objects(稍作解释以删除不必要的位):

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

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

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

于 2013-01-02T03:00:05.183 回答