最近又发现了一个C++ 大坑,是指针解引用的赋值。
解引用之后的赋值操作会造成内存拷贝!!!赋值之后两个变量对应不同的地址!
除非新变量加了引用符号’&’
这一点与Python很不一样,C++ 和 Python的赋值操作("=")有一个重要区别:C++ 的赋值总是默认执行拷贝副本;而Python 为可变类型执行赋值时,默认以引用的方式操作,不会拷贝一份新的副本,
关于两者区别,可以看我另外写的一篇笔记
C++的一个容易出错的地方是,指针解引用之后赋值给一个新的变量,
容易以为新老变量之间共享相同地址,结果发现并不是!
新变量只是一个副本,对新变量的操作不会改变老变量。
比如,
p是指向vector
以下两者写法效果完全不同:
auto vec = *p; // 指针 p 解引用之后,拷贝给vec作为副本
auto& vec = *p; // vec的引用版本,与p所指变量共享地址
- 上面第一种写法,赋值写法,默认拷贝,所以vec 的地址 ≠ p 指向的地址!实际上是相当于把指针p解引用之后的vector拷贝赋值给等号左侧的新变量vec, 所以它们俩的地址不同!
- 第二种写法,引用写法(加了‘&’),不存在拷贝,vec和p所指变量共享内存地址
比如以下代码,本来想在函数中把原vector的所有元素加一,结果没有成功,因为解引用之后赋值导致在新拷贝空间中操作了。
#include#include using namespace std; void vecPlus1(vector * p) { // 错误版本 // 对 vector 的所有元素加1 auto vec = *p; // 指针 p 的解引用, 赋值给vec造成拷贝 for (int64_t i = 0; i != vec.size(); ++i) { vec[i] += 1; } } int main() { void print_(const vector &vec); vector v1 = {1., 2., 3.}; vecPlus1(&v1); // v1 的所有元素加1 cout << "v1 = "; print_(v1); // 输出v1, 结果发现 v1 并没有被改变 cout << endl; return 0; } void print_(const vector &vec) { // print vector's elements for (int64_t i = 0; i != vec.size(); ++i) { cout << vec[i] << ", "; } }
正确版本应该长这样:(直接拿(*p)进行操作,而不是(*p)赋值之后再操作))
正确版本1 void vecPlus1(vector* p) { // 正确版本!!! // 对 vector 的所有元素加1 for (int64_t i = 0; i != (*p).size(); ++i) { (*p)[i] += 1; } } ---------------------------------------------------- 正确版本2 void vecPlus1(vector * p) { // 正确版本!!! auto& vec = *p; // 引用写法,与p所指变量共享地址 // 对 vector 的所有元素加1 for (int64_t i = 0; i != vec.size(); ++i) { vec[i] += 1; } } ---------------------------------------------------- 错误版本 void vecPlus1(vector * p) { // 错误版本!!! // 对 vector 的所有元素加1 auto vec = *p; // 赋值写法,造成*p的拷贝,得到副本vec for (int64_t i = 0; i != vec.size(); ++i) { vec[i] += 1; // 对副本的操作,副本不返回,因此没有意义 } }
对于上面的错误版本(解引用之后的赋值),我们把地址输出出来,也会发现两者的地址并不一致!
比如,
void showAdress(vector* p) { // 显示地址 auto vec = *p; // 指针 p 的解引用 cout << "p = " << p << endl; cout << "&vec = " << &vec << endl; }
输出结果是:
说明指针解引用之后的赋值得到的变量,其地址与原来指针所指地址并不相同,即造成了变量的拷贝
- 指针p解引用看起来没有拷贝,但是加上赋值"="就会造成拷贝
- 为避免拷贝副本,可以用引用的方式
auto& vec = *p;
- 又或者直接对指针p的解引用(*p)进行操作



