为何返回字符串字面量的引用会触发Segmentation fault?
问题分析:悬垂引用与临时对象生命周期差异
先还原两段代码
第一段(触发崩溃的代码)
#include <iostream> #include <string> const std::string& f() { return "abc"; } std::string&& g() { return "xyz"; } int main() { const std::string& s1 = f(); std::string&& s2 = g(); std::cout << s1 << std::endl; // 未定义行为,可能侥幸运行 std::cout << s2 << std::endl; // 触发Segmentation fault return 0; }
第二段(正常运行的代码)
#include <iostream> #include <string> int main() { const std::string& s = "abc"; std::string&& t = "xyz"; std::cout << s << std::endl; std::cout << t << std::endl; return 0; }
崩溃原因
第一段代码的核心问题:悬垂引用
- 函数
f返回const std::string&时,字符串字面量"abc"会被隐式转换为临时std::string对象,函数返回的是这个临时对象的引用。根据C++规则,该临时对象的生命周期仅持续到函数调用表达式结束,也就是f()执行完成后,临时对象就会被销毁。后续s1绑定的是一个已销毁的对象,属于悬垂引用,访问它是未定义行为(可能侥幸运行,也可能崩溃)。 - 函数
g返回std::string&&时,同样会先构造临时std::string对象,返回的是这个临时对象的右值引用。临时对象同样在g()调用结束后销毁,s2成为悬垂引用,后续访问时刚好触发内存访问错误,导致段错误。
两段代码的差异:临时对象生命周期的延长规则
C++有一条关键规则:当临时对象被const左值引用或右值引用直接绑定时,临时对象的生命周期会被延长到与引用变量的生命周期一致。
- 第二段代码中,
const std::string& s = "abc"和std::string&& t = "xyz"都是直接将引用绑定到临时构造的std::string对象,因此临时对象的生命周期被延长到main函数结束,访问时对象仍然有效,所以能正常运行。 - 第一段代码中,引用绑定的是函数返回的引用,而非直接绑定临时对象。函数返回的引用指向的临时对象已在函数返回时销毁,生命周期延长规则不适用,因此引用变成悬垂。
内容的提问来源于stack exchange,提问作者Alexey Starinsky
相关产品推荐
相关产品推荐

