C语言-结构体学习

1,结构体

        把有内在联系的不同变量组织起来,封装成一个整体,这个整体就是结构体(structure)

1.1,结构体类型的声明

 关键字:struct


语法:

struct 结构体名{

        成员列表;

};


*注意:每个成员后要加“;”,结构体的大括号后也要加“;”

例:构造一个商品(product)的结构体类型。

struct Product{         /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/

    char cName[50];     /*商品名称*/
    char cShape[30];    /*商品形状*/
    char cColor[20];    /*商品颜色*/
    char cFunc[50];     /*商品功能*/
    int iPrice;         /*商品价格*/
};

*注意:此时只是定义了一个结构体类型,并未分配其内存单元。简单说就是声明了一种像int,char,double等的类型,还需定义变量才能分配内存单元,才能用。

1.2,结构体变量的定义

        定义结构体全局变量的三种方式

        1,先声明结构体,在定义变量

struct Product{         /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/

    char cName[50];     /*商品名称*/
    char cShape[30];    /*商品形状*/
    char cColor[20];    /*商品颜色*/
    char cFunc[50];     /*商品功能*/
    int iPrice;         /*商品价格*/
};
struct Product product1;    /*定义变量商品1(product1)*/
struct Product product2;    /*定义变量商品2(product2)*/

        2,声明结构体的同时定义变量

struct Product{         /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/

    char cName[50];     /*商品名称*/
    char cShape[30];    /*商品形状*/
    char cColor[20];    /*商品颜色*/
    char cFunc[50];     /*商品功能*/
    int iPrice;         /*商品价格*/
}product1,product2;    /*定义变量商品1(product1),定义变量商品2(product2)*/

3,直接定义结构体类型变量,不需要给出结构体名称

struct{         /*声明商品结构体*/
/*成员名前的首字母表示该成员的类型*/

    char cName[50];     /*商品名称*/
    char cShape[30];    /*商品形状*/
    char cColor[20];    /*商品颜色*/
    char cFunc[50];     /*商品功能*/
    int iPrice;         /*商品价格*/
}product1,product2;    /*定义变量商品1(product1),定义变量商品2(product2)*/

变量内存的计算:成员1+成员2+....

例如:product1的内存单元 = 50+30+20+50+4

结构体的每个变量的内存单元都是相等的

1.3,结构体变量的引用

        引用形式:结构体变量名.成员名

product1.cName = "phone";    /*将产品1的名字改为phone*/
product1.iPrice = 3999;      /*将价格设置为3999*/

对结构体变量惊喜赋值,存取,运算,其实就是对结构体成员进行操作

*注意:不能直接将结构体变量作为一个整体进行输入输出。输入输出也需要像引用形式 “结构体变量名.成员名” 进行输入输出。

如果像下边这种情况:成员本身又属于一个结构体类型。则需要找到最低级成员才能进行操作

例如:

struct data{
    int iYear;     /*年*/
    int iMonth;    /*月*/
    int iDay;      /*日*/
};
struct student{
    char cName[20];    /*姓名*/
    int iNum;          /*学号*/
    char cSex;          /*性别*/
    int iAge;          /*年龄*/
    struct data birthday;    /*生日*/
}student1,student2;

给姓名,学号,年龄,性别赋值时是用:student1.cName = "xxxx"; student1.iNum = 123456等

而要给生日赋值则需要:student1.birthday.iYear = 2025;

生日 与 学号 的赋值相比较,生日 多出了一个data结构体变量 birthday

*注意:输出与赋值一样,怎么赋值的就得怎么输出。不能用结构体变量直接输出所有数据

1.4,结构体类型的初始化

        定义常规变量(如int,char)时,可以直接给变量赋值如:int x = 6; 。结构体类型的变量也一样,在定义结构体变量时直接给结构体变量中的每个成员赋值。这个过程就叫结构体初始化。

struct Student{
    int iAge;
    char cName[64];
    int iyear;
}Student1 = {19 , "LiHua" , 4};  /*将Student1.iAge = 19 ;Student1.cName = "LiHua" ;Student1.iyear = 4*/

*注意:

1,静态存储期(全局或静态变量)
所有未显式初始化的成员会被自动初始化为零(如整数为0,指针为NULL等)

        如上述代码中,将“,4”删掉,printf("学龄:%d\n",Student1.iyear); 。输出的结果将会为0



2,自动存储期(局部变量)

  2.1,未使用任何初始化式:成员值为随机(取决于栈内存的残留数据)。

        2.1.1,若结构体变量完全未初始化(自动存储期),成员值是随机的。

例如:

#include <stdio.h>
struct Student{
    int iAge;
    char cName[64];
    int iyear;
}; 
int main(){
    struct Student Student2 ;    /*这里Student2是没有赋任何值的*/
    printf("姓名:%s\n",Student2.cName);
    printf("年龄:%d\n",Student2.iAge);
    printf("学龄:%d\n",Student2.iyear);
    return 0;
}

 

结果为:

        姓名:
        年龄:4199856
        学龄:0


2.2,使用了初始化式但未覆盖所有成员(如部分初始化):

        2.2.1,若结构体变量有初始化式但未初始化某个成员,该成员值不是随机的,会被置零。

例如:

#include <stdio.h>
struct Student{
    int iAge;
    char cName[64];
    int iyear;
}; 
int main(){
    struct Student Student2 = {2 , "LiHua"};
    printf("姓名:%s\n",Student2.cName);
    printf("年龄:%d\n",Student2.iAge);
    printf("学龄:%d\n",Student2.iyear);
    return 0;
}

结果为:

        姓名:LiHua
        年龄:2
        学龄:0


        2.2.3,静态存储期的结构体变量即使未初始化,成员值也为零。(如使用static修饰结构体变量

例如:

#include <stdio.h>
struct Student{
    int iAge;
    char cName[64];
    int iyear;
}; 
int main(){
    static struct Student Student2 ;
    printf("姓名:%s\n",Student2.cName);
    printf("年龄:%d\n",Student2.iAge);
    printf("学龄:%d\n",Student2.iyear);
    return 0;
}

输出结果为:

        姓名:
        年龄:0
        学龄:0


2,结构体与数组

2.1,定义结构体数组

其定义方法与定义结构体变量一样,可以这样:

struct Student{
    char cName[20];
    int iNumber;
    char cSex;
    int iGrade;
}student[5] ; //定义结构体数组

也可以这样:

struct Student{
    char cName[20];
    int iNumber;
    char cSex;
    int iGrade;
};

int main(){
    struct Student student[5];
    .....
    return 0;
}


student[5]表示这个数组可以存5个学生的信息。

此数组中个数据的储存方式如下:

student[0]cName
iNumber
cSex
iGrade
student[1]cName
iNumber
cSex
iGrade
............
......
......
......

2.2,初始化结构体数组

结构体数组的初始化与结构体变量大致相同,不同的是结构体数组初始化时是赋值一个二维数组,而结构体变量是赋值一个一维数组。

如下:

struct Student{
    char cName[20];
    int iNumber;
    char cSex;
    int iGrade;
}student[5] = {
{"LiHua",1234560,'M'}
{"ZhangSan",1234561,'M'},
{"LiSi",1234562,'W'},
{"WangEr",1234563,'M'},
{"MaZi",1234564,'M'},
} ;

student[5]中的数字5也可以不填,编译器会根据初始化值列表中的元素个数来确认数组的个数。


练习:输出五猫的基本信息如下:

第 1 只猫:
猫名:糖块  猫龄:1个月  猫的体重:4.900000  猫的性别:M

第 2 只猫:
猫名:小点  猫龄:1个月  猫的体重:3.500000  猫的性别:M

第 3 只猫:
猫名:团团  猫龄:1个月  猫的体重:3.900000  猫的性别:W

第 4 只猫:
猫名:琉球  猫龄:1个月  猫的体重:3.300000  猫的性别:M

第 5 只猫:
猫名:点点  猫龄:1个月  猫的体重:3.700000  猫的性别:W

代码:

#include <stdio.h>
struct Cat
{
    char cName[20]; //猫名
    int iAge;       //年龄
    float iWeight;  //体重
    char cSex;      //猫的性别M代表公的,W代表母的
};
int main(){
    struct Cat cat[5]={
        {"糖块" , 1 , 4.90 , 'M'},
        {"小点" , 1 , 3.50 , 'M'},
        {"团团" , 1 , 3.90 , 'W'},
        {"琉球" , 1 , 3.30 , 'M'},
        {"点点" , 1 , 3.70 , 'W'},
    };//定义结构体数组,存储猫的基本信息
    for (int i = 0; i < 5; i++)
    {
        printf("第 %d 只猫:\n",i+1);
        printf("猫名:%s  猫龄:%d个月  猫的体重:%f  猫的性别:%c\n",cat[i].cName , cat[i].iAge , cat[i].iWeight , cat[i].cSex);
        printf("\n");
    }
    
    return 0;
}

3,结构体指针

3.1,指向结构体变量的指针

定义结构体指针与定义结构体变量大致相同如下:

结构体类型   *指针名;

使用方法:

#include <stdio.h>

struct Student{
    char cName[20];
    int iAge;
}student1 = {"李华" , 18};

int main(){

    struct Student *p;
    p = &student1;
    printf("姓名:%s\n",(*p).cName);//p -> cName 与 student.cName ,(*p).cName这三种形式的引用效果是完全等价的
......
}

注意:p -> cName 与 student.cName ,(*p).cName这三种形式的引用效果是完全等价的

使用“ -> ”引用成员时,要注意分析一下情况:

1,p -> iAge;  表示指向的结构体变量中成员iAge的值。

2,p -> iAge++;  表示指向的结构体变量中成员iAge的值,使用后该值加1。

3,++p -> iAge;  表示指向的结构体变量中成员iAge的值加1,计算后再使用。

练习:运用p -> cName 与 student.cName ,(*p).cName这三种形式输出


姓名:张伟
票价:280
乘车区间:成都——上海
车次:D205
乘车时间:2025年02月31日06:50开


代码:

#include <stdio.h>
#include <string.h>
struct Ticket
{
    char cName[20];
    int iNumber;
    char cs[20];
    char cAddress[20];
    char ctime[20];
}ticket;

int main(){
    struct Ticket *p;
    p = &ticket;
    strcpy(p -> cName,"张伟");
    p ->iNumber = 280;
    strcpy(ticket.cs,"成都——上海");
    strcpy(p -> cAddress,"D205");
    strcpy((*p).ctime,"2025年02月31日06:50开");

    printf("姓名:%s\n", p -> cName);
    printf("票价:%d\n", ticket.iNumber);
    printf("乘车区间:%s\n",(*p).cs );
    printf("车次:%s\n",p -> cAddress);
    printf("乘车时间:%s\n",(*p).ctime);
    return 0;
}

3.2,指向结构体数组的指针

1,结构体指针指变量指向结构体数组时,若结构体数组无下标,结构体指针变量的值则是该结构体数组元素的首地址。当然结构体指针也可以直接指向结构体数组中的元素。

struct Student *p;
p = student;

此时输出“ *p ” 则会输出结构体数组 student[0]的值。

2,若结构体指针变量指向结构体数组中的元素时则要这样定义

struct Student *p;
p = &student[2];

输出“ *p ”就等于输出结构体数组student[2]的值。

3,若结构体指针变量指向的结构体数组无下标时,且结构体指针变量p++,(*p)的值则会从student[0]的值变成student[1]的值。

如下所示:

#include <stdio.h>
struct Student
{
    char cName[20]; 
    int iAge;       
    float iWeight;  
    char cSex;      
};
int main(){
    struct Student student[5]={
        {"张三" , 18 , 62 , 'M'},
        {"李四" , 19 , 64 , 'M'},
        {"王二" , 17 , 58 , 'W'},
        {"麻子" , 18 , 66 , 'M'},
        {"李华" , 19 , 53 , 'W'},
    };
    struct Student *p;
    p = student;
    for(int i = 0 ; i < 5 ; i ++ , p ++ ){
        printf("NO %d 名学生:\n",i+1);
        printf("姓名:%s  年龄:%d岁  体重:%f  性别:%c\n",p -> cName , p -> iAge , p -> iWeight , p -> cSex);
        printf("\n");
    }
    return 0;
}

结果:


NO 1 名学生:
姓名:张三  年龄:18岁  体重:62.000000  性别:M

NO 2 名学生:
姓名:李四  年龄:19岁  体重:64.000000  性别:M

NO 3 名学生:
姓名:王二  年龄:17岁  体重:58.000000  性别:W

NO 4 名学生:
姓名:麻子  年龄:18岁  体重:66.000000  性别:M

NO 5 名学生:
姓名:李华  年龄:19岁  体重:53.000000  性别:W


3.3,结构体作为函数参数

1,使用结构体变量作为函数参数

当结构体变量作为实参时,是采用值传递的方式,将结构体变量所占内存的内容全部顺序的传递给形参,同时形参也得是同类型的结构体变量。

参考代码:

#include <stdio.h>

struct Teacher{
    char cName[20];
    int iAge;
    int iSex;
}teacher  = {"葵司" , 25 , 0} ;


/**
 * @brief   输出结构体变量成员数据
 * @param   srt 结构体变量
 * @return  无
 */
void dispy(struct Teacher srt){ //将实参teacher占用内存的内容顺序传递到 srt形参中来
    printf("姓名:%s\n",srt.cName);
    printf("年龄:%d\n",srt.iAge);
    printf("性别:%d\n",srt.iSex);
}

int main(){
    dispy(teacher); //传入实参teacher
    return 0;
}

2,使用指向结构体变量的指针作为函数参数

因为使用结构体变量作为函数参数,在传值过程中会耗费大量的空间和时间。而如果运用的是指向结构体变量的指针来作为函数参数,则传递的是结构体变量的首地址,并没有将变量的副本进行传递,所以与用结构体变量作为函数参数相比会节约出大量时间与空间。

指向结构体变量的指针作为函数参数传递的是结构体变量的地址,如果在函数中改变了结构体变量成员的数据,那么返回主调函数时,结构体变量会发生改变。

参考代码:

#include <stdio.h>

struct Teacher{
    char cName[20];
    int iAge;
    int iSex;
}teacher  = {"葵司" , 25 , 0} ;

/**
 * @brief   输出结构体变量成员数据,并且改变结构体变量teacher中的成员iAge的值
 * @param   *srt 结构体指针,传入的形式也只能是指针形式
 * @return  无
 */
void change_age(struct Teacher *srt){ //创建一个无返回值的函数,且参数为一个指向结构体变量的指针
    printf("姓名:%s\n",srt -> cName);//用指针方式输出结构体变量成员
    printf("年龄:%d\n",srt -> iAge);
    printf("性别:%d\n",srt -> iSex);
    srt -> iAge = 30;//运用指针改变结构体变量成员的值
}

int main(){
    struct Teacher *p = &teacher;//定义一个指向结构体变量teacher的指针 
    change_age(p); //将指针作为参数传递给函数change_age()
    printf("\n");
    printf("修改后的年龄为:%d\n",p -> iAge);
    return 0;
}

4,拓展

4.1,结构体类型函数的使用

结构体还可以作为函数类型来使用,其使用方法是:结构体关键字(struct)+ 结构体名 + 函数名 + (参数1 , 参数2,)。这个函数的返回类型就必须是结构体类型,要么就直接不返回。

示例代码:

#include <stdio.h>
struct Car{
    float x;
};
/**
 * @brief 将传过来的地址里边的值加2
 * @param *p一个结构体指针用于传递地址
 * @return 无
 */

struct Car add( struct Car *q){
    q ->x +=2;
   // return *q; //主函数中没有用到返回值,所以可返回,可不返回,如果需要返回的话,主函数中需要用一个结构体变量来接收此返回值
}


int main(){
    struct Car car = {12};
    struct Car *p = &car;
    struct Car m;
    printf("加油前:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
   
    return 0;
}

4.2,typedef关键字的使用

typedef:是对某一数据类型重新定义(也就是重新给它起个名儿)。

比如,我我感觉 double 这个数据类型的关键字太长了,后边的时候还很多,所以我们就可以用关键字:typedef 

用法:typedef double d;

这样写后,定义double类型的变量时,只需要:d x; 就定义了一个double类型的变量x了。

下面代码与4.1代码的功能相似:

#include <stdio.h>
typedef struct{
    float x;
}Car; //此处的Car并不是结构体的变量,而是将这个结构体类型的名字改为Car

/**
 * @brief 将传过来的地址里边的值加2
 * @param *p一个结构体指针用于传递地址
 * @return 无
 */

Car add(Car *q){
    q ->x +=2;
   // return *q;
}


int main(){
    Car car = {12};
    Car *p = &car;
    printf("加油前:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
    add(p);
    printf("加油后:%.2fL\n",p ->x);
    return 0;
}

对比发现,在定义结构体变量或指针时,少了结构体的关键字,直接用一个Car来替代了 :关键字+结构体名。

4.3,嵌套的结构体

结构体中的成员不仅可以是基本类型,还可以是结构体类型的。

例如,输出班上男生、女生的名字。先定义一个男生名字结构体,成员为普通字符数组类型名字,用来存3名男生的名字,然后定义一个班级所有人的结构体,前三个成员为普通字符数组类型来存女生名字,然后在定义男生名字结构体变量。

代码示例:

#include <stdio.h>
typedef struct {
    char cName1[20];
    char cName2[20];
    char cName3[20];
}boy;
typedef struct {
    char cName1[20];
    char cName2[20];
    char cName3[20];
    boy number;
}class;

int main(){
    class all_number = {"唐磬","王青","彭芸",{"李华","张亮","王浩"}};
    printf("女生有:\n");
    printf("(1)%s \n",all_number.cName1);
    printf("(2)%s \n",all_number.cName2);
    printf("(3)%s \n",all_number.cName3);
    printf("男生有:\n");
    printf("(1)%s \n",all_number.number.cName1);
    printf("(2)%s \n",all_number.number.cName2);
    printf("(3)%s \n",all_number.number.cName3);
    return 0;
}

任务二,实现嵌套两次

例如,输出班上男生与女生的基本信息。先定义一个班结构体,成员为男生、女生,在定义一个男生结构体,成员为每个男生的基本信息(姓名、学号、岁数),最后在定义一个女生结构体成员为每个女生的基本信息(姓名、学号、岁数)。

代码示例:

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

typedef struct {
    char cName[20];
    int iAge;
    int iSno ;
}info;

typedef struct{
    char cClass[20];
    info boy[3];
    info girl[3];
}class;

int main(){
    class no1 = { "初三四班",{
        {"李华", 18 , 22032},
        {"张亮", 19 , 22034},
        {"王浩", 18 , 22036}
    },
        {
        {"唐磬", 17 , 22031},
        {"王青", 18 , 22033},
        {"彭芸", 18 , 22035}
    }
    };
    printf("班级:%s\n",no1.cClass);
    for(int i = 0; i < sizeof(no1.boy) / sizeof(no1.boy[0]); i ++){
        printf("姓名:%s  年龄:%d  学号:%d\n",no1.boy[i].cName, no1.boy[i].iAge, no1.boy[i].iSno );
    }
    for(int i = 0; i < sizeof(no1.girl) / sizeof(no1.girl[0]); i ++){
        printf("姓名:%s  年龄:%d  学号:%d\n",no1.girl[i].cName, no1.girl[i].iAge, no1.girl[i].iSno );
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值