在第二个右大括号之后,b
只能通过间接访问a
。
int main() {
int *a;
{
int b = 42;
a = &b;
}
printf("%d", *a); // UB?
return 0;
}
由于b
不再在范围内,这是 UB 吗?我知道从已经返回的函数中取消引用指向非静态局部变量的指针是 UB,但在这种情况下,一切都在同一个函数中。
这是 C++ 中的 UB,但我不确定 C。
在第二个右大括号之后,b
只能通过间接访问a
。
int main() {
int *a;
{
int b = 42;
a = &b;
}
printf("%d", *a); // UB?
return 0;
}
由于b
不再在范围内,这是 UB 吗?我知道从已经返回的函数中取消引用指向非静态局部变量的指针是 UB,但在这种情况下,一切都在同一个函数中。
这是 C++ 中的 UB,但我不确定 C。
b
仍然在一个不同的嵌套范围内,该范围b
在您访问它时已经退出并因此被破坏。所以它是未定义的行为。同一功能内的一切都是无关紧要的。
出于变量生命周期的目的,您可以将“已返回的函数”视为嵌套范围,反之亦然。
是的,访问任何已达到其生命周期的变量都是未定义的行为。范围和存储时间在这里略有不同。范围更多的是“变量标识符何时可见?” 并且存储持续时间是“变量本身何时存在?”。
您可以拥有范围和持久的事物,例如:
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 的对象具有自动存储持续时间。
对于这样的对象,它的生命周期从进入与其关联的块开始,直到该块的执行以任何方式结束。