在第二个闭合支撑之后,b只能通过间接通过a.
int main() {
int *a;
{
int b = 42;
a = &b;
}
printf("%d", *a); // UB?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
由于b不再是范围,这个UB?我知道从已经返回的函数中取消引用指向非静态局部变量的指针是UB,但在这种情况下,所有内容都在同一个函数中.
这是C++中的UB,但我不确定C.
b仍然在一个不同的,嵌套的范围内,它在b您访问它时已经退出并因此被破坏.所以它是未定义的行为.在同一个功能中的一切都是无关紧要的.
出于变量生命周期的目的,您可以将"已返回的函数"视为嵌套范围,反之亦然.
是的,它是未定义的行为,用于访问任何已达到其寿命终点的变量.范围和存储持续时间在这里略有不同.范围更"变量标识符何时可见?" 存储持续时间是"变量本身何时存在?".
你可以拥有范围和持久性的东西,例如:
int main (void) {
int spoon = 42;
// spoon is both in scope and enduring here
return 0;
}
Run Code Online (Sandbox Code Playgroud)
或超出范围但持久:
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;
}
Run Code Online (Sandbox Code Playgroud)
您还可以使变量超出范围而不会持久,例如:
int main (void) {
// spoon is neither in scope nor enduring here ("there is no spoon")
return 0;
}
Run Code Online (Sandbox Code Playgroud)
事实上,你唯一不能拥有的是范围变量而不是持久.标识符与存储绑定,因为允许没有后备存储的变量没有多大意义.
我不是在谈论指针在这里,这是额外的间接水平-一个在范围内的指针变量总是具有指针值本身存储,即使它指向的东西来已经结束,或者还没有开始,它的存储时间.
未定义的行为在某些情况下可能起作用的事实决不会使行为被定义,事实上这是未定义行为中最烦人的特征之一,因为它有时会起作用.否则,检测起来会容易得多.
在这种特殊情况下,b可变存储持续时间在内部闭合支撑处结束,因此在该点之后尝试访问它是不明智的.
标准的控制部分是c11 6.2.4 Storage duration of objects(稍微解释为删除不必要的位):
对象具有确定其生命周期的存储持续时间.有四个存储持续时间:静态,线程,自动和已分配.
声明标识符没有链接且没有存储类说明符静态的对象具有自动存储持续时间.
对于这样的对象,它的生命周期从入口延伸到与之关联的块,直到该块的执行以任何方式结束.