C 语言结构体成员赋值的深拷贝和浅拷贝

作者 | 李肖遥       责编 | 欧阳姝黎

最近在做通信协议的解析处理、传递分析等问题,总是遇到通信帧中的结构体拷贝等问题,遇到了一些坑,也是比较基础但是易错的C语言知识,一起来探究一下结构体的深拷贝和浅拷贝。

浅拷贝

C语言中的浅拷贝是指在拷贝过程中,对于指针型成员变量只拷贝指针本身,而不拷贝指针所指向的目标,它按字节复制的。我们分几种情况举例子来看一下。

结构体中不存在指针成员变量时

代码如下:

//在win10_64位+vs2017
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>
typedef struct {
    char name[64];
    int age;
}Member;

int main(){
    Member stu1 = { "LiMing", 18 };
    Member stu2;
    stu2 = stu1;
    printf("%s,%d\n", stu2.name, stu2.age);
    system("pause");
    return 0;
}

运行如下:

结构体中存在指针成员变量时

代码如下:

//在win10_64位+vs2017
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1, Member2;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;

 Member2 = Member1;/*拷贝*/
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 printf("%s, %d\n", Member1.name, Member1.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }

 system("pause");
 return 0;
}

运行如下:

从中我们看到,改变 Member2 的值,Member1 的值也改变了,这说明一片空间被两个不同的子对象共享了,改变一个对象的值另外一个也会随之改变。

我们改变 Member2 写法,申请内存的代码如下:

//在win10_64位+vs2017
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;
  
  Member Member2;
 Member2.name = malloc(sizeof(char) * 64);
 if (NULL == Member2.name)
 {
  printf("malloc failed\n");
 }
 memset(Member2.name, 0, 64);
 //strcpy(Member2.name, "LiMing");
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 Member1 = Member2;

 printf("%s, %d\n", Member2.name, Member2.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }
 if (NULL != Member2.name) {
  free(Member2.name);
  Member2.name = NULL;
 }
 system("pause");
 return 0;
}

运行如下:

从中我们看到,当数据成员中有指针时,两个类中的两个指针将指向同一个地址,当对象快结束时,会调用两次 free 函数,此时 Member2 已经是野指针(图中有X的错误标志),这个野指针指向的内存空间已经被释放掉,再次释放会报异常错误,要解决这个问题就要涉及到深拷贝了。

深拷贝

深拷贝除了拷贝其成员本身的值之外,还拷贝成员指向的动态内存区域内容,深拷贝会在堆内存中另外申请空间来储存数据。

解决的思路是在释放掉被赋值指针变量的旧指向内存时,重新对其开辟新内存,这种情况下两个结构体中指针地址不同,但是指向的内容是一致的。代码如下:

//在win10_64位+vs2017
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>
#include <stdlib.h>

typedef struct {
 char *name;
 int age;
}Member;

int main() {
 Member Member1;
 Member1.name = malloc(sizeof(char) * 64);
 if (NULL == Member1.name)
 {
  printf("malloc failed\n");
 }
 memset(Member1.name, 0, 64);
 //strcpy(Member1.name, "LiMing");
 snprintf(Member1.name, 64, "LiMing");
 Member1.age = 18;

  Member Member2;
 Member2.name = malloc(sizeof(char) * 64);
 if (NULL == Member2.name)
 {
  printf("malloc failed\n");
 }
 memset(Member2.name, 0, 64);
 //strcpy(Member2.name, "LiMing");
 snprintf(Member2.name, 64, "LiXiaoYao");
 Member2.age = 29;

 if (Member1.name != NULL) {
  free(Member1.name);
  Member1.name = NULL;
 }
 Member1.name = malloc(strlen(Member2.name) + 1);
 strcpy(Member1.name, Member2.name);

 printf("%s, %d\n", Member1.name, Member1.age);

 if (NULL != Member1.name) {
  free(Member1.name);
  Member1.name = NULL;
 }
 if (NULL != Member2.name) {
  free(Member2.name);
  Member2.name = NULL;
 }
 system("pause");
 return 0;
}

运行如下:

结论

使用C语言来说,深拷贝浅拷贝的概念我们不需要深究,在进行结构体拷贝的时候,结构体成员是非指针的话,那么直接赋值是没有任何问题的,建议使用这种方式,避免浅拷贝这类不易发现的错误产生。

如果成员有指针类型,我们就需要重写拷贝函数,自己定义拷贝行为了,这一点我们需要尤为注意。



☞.NET 开源的免费午餐结束了?☞谷歌中巨大的 SEO 骗局!排名靠前的 HTML 编辑器也不可信
☞“搏一搏,单车变摩托!”华为天才少年耗时四个月,将自行车强势升级为自动驾驶
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值