C语言 结构体的浅拷贝和深拷贝

1. 浅拷贝

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

struct STU
{
	char name[32];
	int age;
	char *address;
};


void struct_init(struct STU* stu)
{
	strcpy(stu->name, "zhangsan", strlen("zhangsan") + 1);
	stu->age = 12;
	stu->address = "beijing";
}

void struct_copy(struct STU *to, struct STU *from )
{
	*to = *from;
}

int main(void)
{ 
	struct STU stu1, stu2;
	memset(&stu1, 0x00, sizeof(struct STU));
	memset(&stu2, 0x00, sizeof(struct STU));
	struct_init(&stu1);

	printf("before copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	struct_copy(&stu2, &stu1);

	printf("\nafter copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	return 0;
}

运行结果:
在这里插入图片描述
结果分析:
如图,未加& 打印的是指针内部存放的值,即指针所指向空间的地址,加了& 打印的是指针的地址。

结构体stu1 和 stu2 被创建的时候,成员name 就有他自己的空间(32字节),也就是说他有自己的地址(&name),所以他们的地址不相同。当struct_copy 开始拷贝name 时,他将from->name 所指向的空间拷贝至to->name,类似于strcpy,所以stu1 和 stu2 他们的内容相同。另外由于name 为字符数组,且数组的第一个成员的地址 和 数组名的地址相同,所以name 和 &name 的地址相同。

再来看adress,当结构体被创建的时候,为每个结构体的指针address分配了空间(32位编译器为4字节),memset将指针所存储的地址初始化为0(如 第一个stu2.address的地址),struct_init 创建了一个常量区的变量“beijing”,并使stu1.address 指向这个变量,所以stu1.address有值,而stu2.address为0。struct_copy 将stu1.address指向的地址(005F7854)拷贝至stu2.address,所以他们打印的值相同。

由于结构体成员address 存放的是常量区的数据,所以就算stu1的空间被释放,stu2的address所指向的空间依旧存在。但是常量区的数据不可被修改,所以我们更多的使用malloc为指针成员分配内存,接下来我们看看malloc对结构体浅拷贝的影响。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

struct STU
{
	char name[32];
	int age;
	char *address;
};


void struct_init(struct STU* stu)
{
	strcpy(stu->name, "zhangsan", strlen("zhangsan") + 1);
	stu->age = 12;
	stu->address = (char *)malloc(strlen("beijing") + 1);
	strcpy(stu->address, "beijing");
}

void struct_copy(struct STU *to, struct STU *from )
{
	*to = *from;
}

int main(void)
{ 
	struct STU stu1, stu2;
	memset(&stu1, 0x00, sizeof(struct STU));
	memset(&stu2, 0x00, sizeof(struct STU));
	struct_init(&stu1);

	printf("before copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	struct_copy(&stu2, &stu1);

	printf("\nafter copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	return 0;
}

与之前不同的是,这次我们没有为address指向常量区的空间,而是在堆区申请了一块空间,再用“beijing”进行填充。

运行结果:
在这里插入图片描述
从结果上来看,和第一个例子在相等关系上似乎没有什么区别。但是,这个程序存在一个巨大的bug,那就是我们malloc的内存没有手动释放,内存泄漏的危害不必多说。我们再来修改一下代码,添加free语句。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

struct STU
{
	char name[32];
	int age;
	char *address;
};


void struct_init(struct STU* stu)
{
	strcpy(stu->name, "zhangsan", strlen("zhangsan") + 1);
	stu->age = 12;
	stu->address = (char *)malloc(strlen("beijing") + 1);
	strcpy(stu->address, "beijing");
}

void struct_copy(struct STU *to, struct STU *from )
{
	*to = *from;
}

int main(void)
{ 
	struct STU stu1, stu2;
	memset(&stu1, 0x00, sizeof(struct STU));
	memset(&stu2, 0x00, sizeof(struct STU));
	struct_init(&stu1);

	printf("before copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	struct_copy(&stu2, &stu1);

	printf("\nafter copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	if (stu1.address)
	{
		printf("\nbefore free\n");
		printf("stu1.address:%p\n", stu1.address);
		printf("&stu1.address:%p\n", &stu1.address);

		free(stu1.address);
		//stu1.address = NULL;

		printf("\nafter free\n");
		printf("stu1.address:%p\n", stu1.address);
		printf("&stu1.address:%p\n", &stu1.address);
	}

	if (stu2.address)
	{
		printf("\nbefore free\n");
		printf("stu2.address:%p\n", stu2.address);
		printf("&stu2.address:%p\n", &stu2.address);

		free(stu2.address);
		//stu2.address = NULL;

		printf("\nafter free\n");
		printf("stu2.address:%p\n", stu2.address);
		printf("&stu2.address:%p\n", &stu2.address);
	}

	return 0;
}

在这里插入图片描述
通过添加断点,我们发现在程序运行到 free(stu2.address); 时,就崩溃了。而且我们可以从打印信息中看出,stu1.address 的值在free前后虽然没有变化,但是这块地址所对应的空间确实被系统回收了,而当free(stu2.address)时,由于和stu1.address 指向的地址相同,所以stu2 free的就是属于系统的空间,所以程序会崩溃。

2. 深拷贝

那么,如果结构体中有这种变量的话,那应该怎么拷贝呢。我们再来看一段代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

struct STU
{
	char name[32];
	int age;
	char *address;
};


void struct_init(struct STU* stu)
{
	strcpy(stu->name, "zhangsan", strlen("zhangsan") + 1);
	stu->age = 12;
	stu->address = (char *)malloc(strlen("beijing") + 1);
	strcpy(stu->address, "beijing");
}

void struct_copy(struct STU *to, struct STU *from )
{
	//*to = *from;
	memcpy(to, from, sizeof(struct STU));
	to->address = (char *)malloc(strlen(from->address) + 1);
	strcpy(to->address, from->address);
}

int main(void)
{ 
	struct STU stu1, stu2;
	memset(&stu1, 0x00, sizeof(struct STU));
	memset(&stu2, 0x00, sizeof(struct STU));
	struct_init(&stu1);

	printf("before copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	struct_copy(&stu2, &stu1);

	printf("\nafter copy\n");
	printf("stu1.name:%p\n", stu1.name);
	printf("&stu1.name:%p\n", &stu1.name);
	printf("stu2.name:%p\n", stu2.name);
	printf("&stu2.name:%p\n", &stu2.name);
	printf("stu1.address:%p\n", stu1.address);
	printf("&stu1.address:%p\n", &stu1.address);
	printf("stu2.address:%p\n", stu2.address);
	printf("&stu2.address:%p\n", &stu2.address);

	if (stu1.address)
	{
		printf("\nbefore free\n");
		printf("stu1.address:%p\n", stu1.address);
		printf("&stu1.address:%p\n", &stu1.address);

		free(stu1.address);
		//stu1.address = NULL;

		printf("\nafter free\n");
		printf("stu1.address:%p\n", stu1.address);
		printf("&stu1.address:%p\n", &stu1.address);
	}

	if (stu2.address)
	{
		printf("\nbefore free\n");
		printf("stu2.address:%p\n", stu2.address);
		printf("&stu2.address:%p\n", &stu2.address);

		free(stu2.address);
		//stu2.address = NULL;

		printf("\nafter free\n");
		printf("stu2.address:%p\n", stu2.address);
		printf("&stu2.address:%p\n", &stu2.address);
	}

	return 0;
}

我们修改了struct_copy函数,这次先将from的内容全部拷贝至to,再针对to->address 变量单独分配空间,并用from->address 将其初始化,这样看起来from跟to的内容一模一样了,也就完成了结构体的拷贝。

运行结果:
在这里插入图片描述
根据打印信息我们看出,这此stu2.address 的地址跟之前两次不同,不再和stu1.address 相同,说明两个指针指向的不再是同一块空间,针对不同的空间,我们将他们单独释放,程序也就不会出错了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值