使用已超出作用域的捕获变量地址(不解引用)是否属于未定义行为?
关于悬垂变量地址使用的两个问题解答
问题梳理
- 不解引用的情况下,使用悬垂变量的地址算不算未定义行为?
- 通过lambda按引用捕获的变量,在变量超出作用域后,只是使用它的地址(不解引用),算不算未定义行为?
结合代码示例分析
先看你给出的代码:
#include <iostream> auto foo() { int i = 42; return [&] { std::cout << &i << '\n'; }; } int main() { auto lmb = foo(); lmb(); }
首先明确:这个lambda是按引用捕获i的,它内部保存的其实就是i的内存地址(或者说绑定到i的引用)。但当foo()执行完返回时,栈上的局部变量i会被销毁,它原来占用的内存会被回收,此时这个地址就成了悬垂地址——没有有效对象关联的地址。
1. 不解引用悬垂地址的行为
在C++规则里,哪怕只是访问已销毁对象的地址(不管解不解引用),都属于未定义行为。原因很简单:对象销毁后,它的地址已经不再对应任何合法的对象,标准不保证对这个地址的任何操作是安全的。编译器可能直接优化掉这段代码,或者运行时打印出无意义的地址,甚至触发内存报错,一切都不确定。
2. lambda访问已销毁捕获变量的地址
回到你的代码:lambda在foo()里创建时,确实已经拿到了i的地址并保存了下来,但问题是i的生命周期结束后,这个地址就失效了。此时调用lambda打印&i,本质就是在访问一个已销毁对象的地址,同样属于未定义行为。
你疑惑的“lambda是否在变量存在时捕获地址并存储”——没错,确实是这样,但关键在于变量销毁后,这个地址已经没有合法的对应对象了,任何使用它的操作都不受标准保护。
总结
不管是直接用悬垂变量的地址,还是通过引用捕获的lambda去访问已销毁变量的地址,哪怕不解引用,这些操作都是C++标准定义的未定义行为,程序的表现完全不可预料,绝对不能依赖这种写法。
内容的提问来源于stack exchange,提问作者Tootsie
相关产品推荐
相关产品推荐

