栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 大坑 指针解引用之后的赋值造成拷贝

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 大坑 指针解引用之后的赋值造成拷贝

C++ 大坑 指针解引用之后的赋值造成和原来的地址不一致

最近又发现了一个C++ 大坑,是指针解引用的赋值。
解引用之后的赋值操作会造成内存拷贝!!!赋值之后两个变量对应不同的地址!

c++ 的赋值操作("="号的操作)总是默认执行拷贝副本!

除非新变量加了引用符号’&’

这一点与Python很不一样,C++ 和 Python的赋值操作("=")有一个重要区别:C++ 的赋值总是默认执行拷贝副本;而Python 为可变类型执行赋值时,默认以引用的方式操作,不会拷贝一份新的副本,
关于两者区别,可以看我另外写的一篇笔记

C++的一个容易出错的地方是,指针解引用之后赋值给一个新的变量,
容易以为新老变量之间共享相同地址,结果发现并不是!
新变量只是一个副本,对新变量的操作不会改变老变量。
比如,
p是指向vector v1的指针,
以下两者写法效果完全不同:

 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)进行操作
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/604223.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号