C++中函数返回对象时局部与主函数变量地址相同及注释成员后不同的原因
C++返回局部对象时地址相同的原因解析
示例代码
#include<queue> using namespace std; class node { public: int data; node* left; node* right; // node(){} node(int d) { this -> data = d; this -> left = NULL; this -> right = NULL; } }; node buildTree() { cout << "Enter the data: " << endl; int data = 2; // cin >> data; node r(data); node left(4); cout<<&r<<endl; return r; } int main() { cout<<"print"; node root = buildTree(); cout<<&root<<endl; return 0; }
问题
在C++中,buildTree函数内的局部变量r与main函数中的变量root地址为何相同?而当注释node类的left和right数据成员后,两者地址却不同,这是什么原因?
原因解析
这事儿核心是C++编译器的**返回值优化(RVO,具体是命名返回值优化NRVO)**在起作用,本质是编译器为了减少不必要的对象拷贝搞的性能优化操作。
1. 保留left和right成员时地址相同的情况
当node类包含left和right两个指针成员时,对象的尺寸足够大(比如32位系统下是12字节:一个int加两个4字节指针,64位系统是20字节),编译器会触发NRVO:
- 它不会在
buildTree的函数栈帧里单独构造局部对象r,而是直接在main函数里root的内存位置上构造r。相当于跳过了“先创建局部对象再拷贝到main栈帧”的冗余步骤。 - 既然
r和root共用同一块内存空间,它们的地址自然完全一致。
2. 注释left和right后地址不同的情况
当node类只剩int data这一个成员时,对象尺寸极小(仅4字节),编译器会选择另一种更高效的优化方式:
- 直接把局部对象
r的data值存入CPU寄存器,函数返回时直接传递这个寄存器的值;回到main函数后,再用寄存器里的值在root的内存位置构造新对象。 - 这时
r是buildTree栈帧内的局部变量,root是main栈帧内的变量,二者处于不同的栈内存区域,地址也就不一样了。
额外补充:NRVO是编译器可选的优化手段,属于C++标准允许的“省略拷贝/移动构造函数”的场景。如果你使用-fno-elide-constructors这类关闭优化的编译选项,就能看到编译器老老实实执行对象拷贝流程,此时两个变量的地址就会不同。
内容的提问来源于stack exchange,提问作者Niravkumar Butani
相关产品推荐
相关产品推荐

