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 相同,说明两个指针指向的不再是同一块空间,针对不同的空间,我们将他们单独释放,程序也就不会出错了。