第四天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
原创粉丝点击