前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >结构体成员赋值到底是深拷贝还是浅拷贝?

结构体成员赋值到底是深拷贝还是浅拷贝?

作者头像
编程珠玑
发布2020-02-11 16:45:21
3.2K0
发布2020-02-11 16:45:21
举报
文章被收录于专栏:编程珠玑

来源:公众号【编程珠玑】

作者:守望先生

ID:shouwangxiansheng

在《C语言容易忽略的知识点》一文中,有读者说这种结构体复杂成员赋值的的拷贝是浅拷贝(感谢读者提出),那么到底什么是深拷贝,什么是浅拷贝?

浅拷贝

浅拷贝指的是仅拷贝对象的所有成员,而不包括其引用对象(例如指针指向的其他内容)。我们来看C和C++的例子。

C++的例子如下:

代码语言:javascript
复制
//来源:公众号【编程珠玑】
//https://www.yanbinghu.com
#include <iostream>
class Test
{
public:
    /*构造函数*/
    Test():a(0)
    {
        std::cout<<"new b"<<std::endl;
        b = new char[16];
    }
    /*析构函数*/
    ~Test()
    {
        std::cout<<"delete b"<<std::endl;
        delete [] b;
    }
private:
    int a;
    char *b;
};
int main()
{
    Test test;
    Test test0 = test;//浅拷贝
    return 0;
}

运行结果:

代码语言:javascript
复制
new b                                                          
delete b                                                         
delete b
core dumped

可以看到,在拷贝test的时候,只拷贝了其成员本身的值,即a和b的值,而b只是一个指针,它指向的内容却没有被拷贝,因此我们说,它是浅拷贝。而对象test在被销毁时,会释放b指向的内存,test0被销毁时,又进行了重复释放,因此导致core dumped。

其拷贝过程如下图所示:

浅拷贝

C语言例子:

代码语言:javascript
复制
//来源:公众号编程珠玑
//https://www.yanbinghu.com
#include<stdio.h>
#include<stdlib.h>
typedef struct Member_t
{
    char *p;
    int c;
}Member;
typedef struct Test_t
{
    int a;
    Member b;
}Test;
int main(void)
{
    Test test0;
    test0.a = 10;
    test0.b.p = malloc(16);
    if(NULL == test0.b.p)
    {
        printf("malloc failed\n");
    }
    snprintf(test0.b.p,16,"hello");
    test0.b.c = 24;

    /*拷贝*/
    Test test1;
    test1.a = test0.a;
    test1.b = test0.b;

    /*修改*/
    snprintf(test1.b.p,16,"world");

    printf("%s\n",test0.b.p);
    free(test0.b.p);
    test0.b.p = NULL;
    return 0;

}

运行结果:

代码语言:javascript
复制
world

由于其结构体成员赋值时,只拷贝其成员本身的值,即

代码语言:javascript
复制
test1.b = test0.b

只拷贝了其中的p的值和c的值,却没有拷贝p指向的内存,因此拷贝之后,两者的p指向同一片内存区域,导致通过其中一个修改就会影响另外一个的内容。因此它也是浅拷贝。(感谢在上篇中读者指出)

深拷贝

深拷贝除了拷贝其成员本身的值之外,还拷贝的成员指向的动态内存区域等类似的内容。 那么对于前面的例子,我们如何进行深拷贝呢?以C++为例,我们需要定义自己的拷贝构造函数:

代码语言:javascript
复制
Test(Test &t)
{
    std::cout<<"copy"<<std::endl;
    a = t.a;
    b = new char[16];
    /**拷贝b指向的内容**/
    memcpy(b,t.b,16);
}

这里就不是拷贝指针b的值,而是拷贝指针b指向的内容。因此是深拷贝。再次运行结果:

代码语言:javascript
复制
new b     
copy
delete b                                                         
delete b

这种情况下,test和test0中b的值是不一样的,但是b指向的内容是一样的。

那么C语言中怎么处理呢?自然就是需要拷贝成员b中p指向的内容了。这里就留给读者自己去实现了。

深拷贝过程如下:

深拷贝

C语言里的深拷贝与浅拷贝

作为使用C语言的读者来说,我觉得到没有必要去抓什么深拷贝与浅拷贝的概念,你只需要理解,C里面的赋值类的拷贝,仅仅是拷贝值而已,比如你拷贝的是指针,那么只是拷贝指针的值,指针指向的区域是不会拷贝的;而如果你拷贝的是数组,那么将会拷贝数组的值,而不是数组首地址(参考《C语言容易忽略的知识点》中的例子)。

结构体赋值

那么回到结构体赋值成员赋值的问题。根据上面的分析可以知道,如果结构体成员都是基本数据类型或者数组(非指针),那么直接赋值是没有任何问题的,而且非常地方便,而如果成员有指针类型,你又不想复制的结构体成员指向相同的内存区域,那么你就需要自己拷贝其指向的内容。

关于数组和指针,请参考《数组之谜》。

总结

默认的拷贝行为基本都是浅拷贝,即仅仅拷贝其成员值。当然如果所有成员值没有引用任何外部对象,或者引用的外部对象定义了自己的深拷贝行为,那么深拷贝和浅拷贝是一样的。如果需要拷贝值以外的内容,请自己定义拷贝行为。

最后,一张图理解深拷贝和浅拷贝:

深拷贝和浅拷

最后关于C语言,自动动手,丰衣足食。

另外,有些概念是为了更好说明某个点,如果这个概念不能帮助你理解这个点,那么请关注这个点本身。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浅拷贝
  • 深拷贝
  • C语言里的深拷贝与浅拷贝
  • 结构体赋值
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档