第四天2017/03/31(下午1:结构体、数组)
来源:互联网 发布:linux集群搭建报告 编辑:程序博客网 时间:2024/04/30 07:35
//备用知识:没有内存,哪有指针?int main(){//错误程序 char *name; //此处只定义了指针name(指针占4个字节),并没有给name分配内存 //name = (char*)malloc(100*sizeof(char)); cin>>name; //因此:往name中写数据,程序肯定会运行崩溃! cout<<name;}修改成下面的程序:int main(){//正确程序 char *name; //此处只定义了指针name(指针占4个字节),并没有给name分配内存 name = (char*)malloc(100*sizeof(char));//动态给name指针分配内存 cin>>name; //因为name已经分配完内存,可以往里边写数据 cout<<name;}int main(){//正确程序 char name[100];//静态给name分配内存 cin>>name; //因为name已经分配完内存,可以往里边写数据 cout<<name;}//============================================================//知识延伸://cin何时能写入?结构体指针和结构体成员指针分配内存的知识、释放内存的顺序? 结构体里边的二级指针#include <iostream>using namespace std;typedef struct student{ char **ptr; //【❤结构体里边的二级指针ptr】 char na[100]; //给na数组静态分配了内存 char *name; //没有给name指针分配内存 int age;}student;int main(){ student *s1 = (student*)malloc(sizeof(student)); cin >>s1->na; cout<<s1->na; //cin>>s1->name; //程序崩溃:【知识点】给结构体指针s1分配了内存,并不表示给结构体中的成员name也分配内存 //cout<<s1->name; s1->name = (char *)malloc(100*sizeof(char)); //再给结构体中的name单独分配内存 cin>>s1->name; cout<<s1->name;//释放内存:s1指针和s1->name指针必须单独释放//【重点:释放顺序】先释放里边的s1->name,再释放外边的s1。 if(s1!=NULL) { free(s1); s1 = NULL; } if(s1->name) { free(s1->name); s1->name = NULL; } return 0;}
学习模块:结构体
(1)结构体变量之间的相互赋值
①赋值运算符②memcpy(&s1,&s2,sizeof(s2));③逐个元素的手动copy
(2)结构体作函数参数的两种case
①用结构体变量作形参②用结构体指针、引用作形参【二者有天壤之别】区别一:(在被调函数中修改后,主调函数的结构体中的内容是否也被修改)①是传值调用②传址调用,改变了指针变量指向的内容的值区别二:(效率问题)①发生赋值运算符之间的拷贝,消耗更大的,不建议使用;应用②更好。
例:
(3)结构体中有二级指针、结构体数组相结合的代码
【注】结构体中的二级指针一般用“二级指针的内存模型三”
#include <iostream>using namespace std;typedef struct person{ char *pname; //char *name[100]; int age; char **ptr; //二级指针}person; //函数接口的声明person* create_PArr1(int N,int num); //创建结构体数组int create_PArr2(person **per,int N,int num);int initPerArr(person* per,int N,int num); //初始化结构体数组int PrintPerArr(person* per,int N,int num); //打印结构体数组void free_person1(person *p,int N,int num); //释放结构体数组中分配的指针void free_person2(person **p,int N,int num); int main(){ int rev = 0; //0表示程序运行正确 int N = 3; int num = 2; person *per = NULL;//创建结构体数组方式一:通过返回结构体指针 //per = create_PArr1(N,num); //动态创建结构体数组(3个元素),每个元素中的二级指针指向2个字符串 //if(per==NULL) //{ // goto END; //}//创建结构体数组方式二:通过实参是结构体一级指针的地址,形参是结构体二级指针 rev = create_PArr2(&per,N,num); if(rev!=0) { goto END; //用goto语句避免内存泄漏 } //初始化结构体数组 rev = initPerArr(per,N,num); if(rev!=0) { goto END; //用goto语句避免内存泄漏 } //打印出结构体数组 rev = PrintPerArr(per,N,num); if(rev != 0) { goto END; //用goto语句避免内存泄漏 }END: //free_person1(per,N,num); //此种方式会导致野指针 //cout<<"释放完毕,程序结束!"<<endl; free_person2(&per,N,num); //传入per的地址&per,可以在被调函数中修改per的值,即 per = NULL; cout<<"释放完毕,程序结束!"<<endl; getchar();}//==========================================================================================================//创建结构体数组方式一:(在被调函数中分配内存,通过return把在被调函数中分配的结构体数组的指针返回到主调函数中)person* create_PArr1(int N,int num) //创建结构体数组,数组中有N个结构体元素{ person* per = (person*)malloc(N*sizeof(person)); //类似于int* array = (int*)malloc(N*sizeof(int)); //动态分配内存创建数组person per[N]; if(per==NULL) { cout<<"创建数组失败"<<endl; return NULL; } //给结构体数组中的成员分配内存空间 for(int i=0;i<N;i++) { //给结构体数组中的一级指针成员pname分配内存空间 per[i].pname = (char*)malloc(100*sizeof(char)); if(per[i].pname==NULL) return NULL; //给结构体数组中的二级指针成员ptr分配内存空间 per[i].ptr = (char**)malloc(num*sizeof(char*)); //num表示二级指针指向一级指针的个数 if(per[i].ptr==NULL) return NULL; for(int j=0;j<num;j++) //每个per[i]中的ptr指针指向一级指针的个数,per[i].ptr[num] { per[i].ptr[j] = (char*)malloc(100*sizeof(char)); if(per[i].ptr[j]==NULL) return NULL; } } return per; //在堆中分配的内存可以返回到main函数}//创建结构体数组方式二:(通过二级指针作为形参,一级指针作为实参)int create_PArr2(person **per,int N,int num) //创建结构体数组,数组中有N个结构体元素{ if(per==NULL) //如果传入的地址是无效值 return -1; person *ptemp = NULL; //定义一个临时的结构体指针 ptemp = (person *)malloc(N*sizeof(person)); //给ptemp指针分配内存,让ptemp指向一个动态分配的结构体数组,相当于ptemp[N] if(ptemp==NULL) return -1; for(int i=0;i<N;i++) { ptemp[i].pname = (char *)malloc(100*sizeof(char)); if(ptemp[i].pname==NULL) return -1; ptemp[i].ptr = (char **)malloc(num*sizeof(char*)); if(ptemp[i].ptr==NULL) return -1; for(int j=0;j<num;j++) { ptemp[i].ptr[j] = (char*)malloc(100*sizeof(char)); if(ptemp[i].ptr[j]==NULL) return -1; } } *per = ptemp; //方式二:在被调函数中定义一个临时的变量ptemp,给ptemp分配完空间后,在把ptemp赋值给形参*per即可 //主调函数传入的是地址 return 0;}//==========================================================================================================int initPerArr(person* per,int N,int num) //传入的是一级指针{ if(per==NULL) //如果传入的指针per是无效值 return -1; for(int i=0;i<N;i++) { per[i].age = 20+i; sprintf(per[i].pname,"pname = %d",20+i); for(int j=0;j<num;j++) { sprintf(per[i].ptr[j],"pname = %d , %d",20+i,j); } } return 0;}int PrintPerArr(person* per,int N,int num){ if(per==NULL) //如果传入的指针per是无效值 return -1; for(int i=0;i<N;i++) { per[i].age = 20+i; cout<<per[i].age<<" , "<<per[i].pname<<" , {"; for(int j=0;j<num;j++) { cout<<per[i].ptr[j]<<" "; } cout<<"}"<<endl; } return 0;}//==========================================================================================================//内存释放方式一:这种方法在进行释放完成后,(因为是值传递,所以)无法把主调函数中的传入的实参设置为NULLvoid free_person1(person *p,int N,int num) //释放N个结构体指针{ for(int j=0;j<N;j++) { if(p[j].pname!=NULL) { free(p[j].pname); p[j].pname = NULL; } if(p[j].ptr!=NULL) { for(int i=0;i<num && p[j].ptr[i]!=NULL;i++) { free(p[j].ptr[i]); p[j].ptr[i] = NULL; } } free(p[j].ptr); p[j].ptr = NULL; }}void free_person2(person **p,int N,int num) //因为通过方式二在进行释放完后,可以顺便把实参的值也设置为NULL,避免野指针的发生{ for(int j=0;j<N;j++) { if((*p)[j].pname!=NULL) { free((*p)[j].pname); (*p)[j].pname = NULL; } if((*p)[j].ptr!=NULL) { for(int i=0;i<num && (*p)[j].ptr[i]!=NULL;i++) { free((*p)[j].ptr[i]); (*p)[j].ptr[i] = NULL; } } free((*p)[j].ptr); (*p)[j].ptr = NULL; } //方式二的优点 p = NULL; //因为传进去的实参被释放后全都没有了,为了避免野指针的发生,在释放完所有的(*p)指向的内容 //后,把指针p也设置为NULL}//==========================================================================================================
(4)【❤重要】
【浅拷贝】
浅拷贝发生的情形:当结构体中有指针类型的数据元素时,如果使用默认的拷贝构造,会发生浅拷贝。
【带来的问题】在进行指针释放时,释放两次内存造成程序运行崩溃。
【深拷贝】
可以消除默认拷贝构造带来的浅拷贝情形,使程序正常运行。
【例】#include <iostream>using namespace std;typedef struct person{ char *pname; int age;}person;person* creat_person(){ person *p = NULL; p = (person*)malloc(sizeof(person)); if(p==NULL) return NULL; p->pname = (char*)malloc(sizeof(char)*100); if(p->pname==NULL) return NULL; return p;}void init_person(person* p){ p->age = 25; //p->pname = "Mr.right"; //【注】在进行字符串初始化操作时,用strcpy,不要用等号运算符 strcpy(p->pname,"Mr.right");}void print_person(person* p){ cout<<p->age<<" "<<p->pname<<endl;}void free_person(person* p){ if(p!=NULL) { if(p->pname!=NULL) { free(p->pname); p->pname = NULL; } p = NULL; }}void copy_person(person* from,person* to){ memcpy(to,from,sizeof(struct person));//从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中 //深拷贝 to->pname = (char*)malloc(sizeof(char)*100); //给to->pname分配额外的空间 strcpy(to->pname,from->pname);}int main(){ person *p1 = creat_person(); //p1、p2是指针变量,而不是person结构体对象 person *p2 = creat_person(); //*p1、*p2是person结构体对象 init_person(p1); init_person(p2); print_person(p1); print_person(p2); //(*p2) = (*p1); //结构体对象赋值(【注】浅拷贝) //p2 = p1; //此句话表示:结构体对象赋值,而不是指针赋值 copy_person(p1,p2);//【注】深拷贝 print_person(p1); print_person(p2); free_person(p1); free_person(p2); //浅拷贝导致程序崩溃 getchar();}
1 0
- 第四天2017/03/31(下午1:结构体、数组)
- 第四天2017/03/31(下午2:结构体、数组)
- Android 第四天 (下午)
- 2017.8.3暑假集训第四天(下午练习赛)
- 培训第四天,选择结构和数组
- 第四天2017/03/31(上午:指针、数组的小知识)
- C#学习第四天 常量、枚举、结构、数组
- 1-23下午方法和数组
- 第四章-结构体
- 黑马程序员-第四天(数组)
- 第四天-数组、排序
- 第四天 数组
- 数组-第四天
- 第四天 数组
- 2017.8.1暑假集训第二天(下午训练赛)
- 程序设计基础(C&C++) 戴波、张东祥 第四章 数组与结构 编程作业
- MOOC 程序设计基础(C&C++) 戴波、张东祥 第四章 数组与结构 作业
- 数组--结构体数组
- 软件测试分类
- Oracle11g RAC下真的要关闭DRM
- 安卓gson-fomat 插件 根据json自动生成实体类
- 企业上架发布-工作中遇到的一点问题
- 安卓 RecyclerView
- 第四天2017/03/31(下午1:结构体、数组)
- Ubuntu 配置jdk
- MySQL5.6在线DDL需要注意的问题
- Ubuntu 开启桌面小图标
- 图片网址
- JNDI DataSource案例
- git通过命令本地代码上传github
- 【活动】一杯云雾 品味春意
- 安卓代码段--传递Bundle数据