在第二个右大括号之后,b
只能通过a
间接访问。{/ p>
int main() {
int *a;
{
int b = 42;
a = &b;
}
printf("%d", *a); // UB?
return 0;
}
由于b
不再是范围,这个UB是不是?我知道从已经返回的函数中取消引用指向非静态局部变量的指针是UB,但在这种情况下,所有内容都在同一个函数内。
这是C ++中的UB,但我不确定C。
答案 0 :(得分:5)
b
仍在一个不同的嵌套范围内,当您访问它时,该范围已退出并因此被销毁b
。所以它是未定义的行为。在同一个功能中的一切都是无关紧要的。
出于变量生命周期的目的,您可以将“已返回的函数”视为嵌套范围,反之亦然。
答案 1 :(得分: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
(稍微解释为删除不必要的位):
对象具有确定其生命周期的存储持续时间。有四个存储空间 durations:static,thread,automatic和assigned。
声明标识符没有链接且没有存储类说明符静态的对象具有自动存储持续时间。
对于这样的对象,它的生命周期从入口延伸到与之关联的块,直到该块的执行以任何方式结束。