1.结构体中指针变量的深拷贝和浅拷贝
疑问点记录:
char *p = (char *)malloc(100);
strcpy(p,"renzhenming");
这两行代码定义了一个字符指针p,他指向堆内存中一段空间,这一段空间存储了一个字符串renzhenming,那么既然这个指针指向了这块内存,那么我通过取指针元素符号*应该就可以得到这块内存中的元素了,也就是说按照猜想我打印*p
printf("%s",*p)
应该可以得到值renzhenming这个字符串才对,事实上程序却崩溃了,正确的写法是这样
printf("%s",p);
才能得到我们想要的结果:打印出renzhenming这个字符串
为什么?
我想很多初学者可能会有这种疑问,其实深入剖析一下你就会明白了,字符指针char *p他是指向字符的,看下边的代码
char a = 'c';
char *p=&a;
printf("%c",\*p);
这样是可以打印出c这个字符,为什么char *p是字符指针却可以指向一个字符串?c语言是使用字符数组来表示和存储字符串的,所以可以把字符串和字符数组等价起来,在数组中,数组名表示的就是数组首元素的地址,那么对字符数组来说,数组名表示的就是字符数组中首个字符的地址,因为字符指针可以保存字符的地址,那么就可以使用字符指针来保存字符数组的首个字符的地址,也就是字符数组名
也就是说
char temp[100] ="renzhenming"
和
char p = "renzhenming"
表示是同一个意思,那么字符指针就是可以指向一个字符数组,也就是可以指向一个字符串。数组名temp和p都是表示的字符串renzhenming的第一个字符 r 的地址。
那么此时,你就会明白,为什么p不能把整个字符串打印出来了,因为*p表示的是第一个字符r,你通过%s去接收一个字符,会导致程序崩溃,如果你这样打印
printf("%c",*p)
就能打印出r这个字符了。
为什么通过%s去接收一个字符会导致程序崩溃?
因为%s格式字符串会从给定的内存空间开始向后逐个输出字符,直到遇到\0结束。格式字符串为%s时,后面的参数应该为一个内存地址(指针),如果给出的是一个字符变量,那么会将字符变量中的值认作地址,例如字符变量ch中保存的是字符'a',那么printf会将'a'对应的ASCII码97作为内存地址,试图转到该位置读出数据;而该位置的内存空间属于操作系统,为保护段空间不可访问,因而程序崩溃
那么为什么printf("%s",p)可以打印出整个字符串?我们知道字符串是以\0为结束标志的,p表示字符串第一个字符的地址,当你通过%s去打印的时候,他会自动向后移动指针,直到找到\0结束符为止,将从p这个为止开始到结束符为止的字符全部打印出来,就是最终的结果了。
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Worker {
int age;
char name[100];
char *alias;
} Worker;
/*
浅拷贝
*/
void lightCopy(Worker *temp1, Worker *temp2) {
//*temp1 = *temp2;
memcpy(temp1, temp2, sizeof(Worker));
//上边两种拷贝方式是相同的
printf("sizeof(temp2)=%d\n", sizeof(Worker));
}
void deepCopy(Worker *temp1, Worker *temp2) {
memcpy(temp1, temp2, sizeof(Worker));
temp1->alias = (char *)malloc(100);
strcpy(temp1->alias, temp2->alias);
}
void main() {
Worker w1;
Worker w2;
//给w1赋值
strcpy(w1.name, "renzhenming");
w1.alias = (char *)malloc(100);
strcpy(w1.alias, "ren");
//lightCopy(&w2, &w1);
deepCopy(&w2, &w1);
printf("w2.name = %s,w2.alias = %s\n", w2.name, w2.alias);
//释放申请的内存
if (w1.alias != NULL) {
free(w1.alias);
w1.alias = NULL;
}
if (w2.alias != NULL) {
free(w2.alias);
w2.alias = NULL;
}
system("pause");
}
简单来说,深拷贝和浅拷贝针对的是指针,浅拷贝拷贝的是指针的地址值,而深拷贝是拷贝的指针指向的内存。
编译器针对结构体中指向一块堆内存空间的指针进行等号操作,既*p1 = *p2,操作的结果是将p2的地址拷贝到了p1上此时p2和p1指向同一块内存空间。那么此时,由于执行浅拷贝,当free空间的时候,先判断if (w1.alias != NULL)满足,则将alias指向的空间释放,再判断if (w2.alias != NULL) 仍然满足,因为w2的alias指针和w1的alias指针并不是同一个,但是所指向的空间是同一个,这就导致free空间的时候由于已经被free,再次释放发生崩溃。深拷贝则可以避免这个问题。