C语言-结构体详解

引入

  • 我们前面看到的数据类型

    • int
    • double
    • float
    • char
    • 数组
    • 指针
    • .....
  • 不同的数据类型的变量是用来报错不同类型的数据的,那么请声明一个变量,用来保存一个人的年龄

    • 思考下,年龄目前看不可能有负数的,最大也有 200 左右吧,那么占用 1 个字节就可以了

    • unsigned char age = 18

    • int age = 18

    • 声明一个人的身高用 float,等

    • 一个变量就来存储一个数据,描述这个数据

    • 请声明一个变量来保存一个学生,那用什么类型来保存这个学生信息呢?

    • 但是我们之前学习的任何一个变量都不行

    • 一个学生的信息:

      • 姓名:字符串
      • 年龄:int
      • 性别:字符串
      • 成绩:int
    • 一个学生的信息至少需要上面的那几个数据联合起来描述的,如果我们要标识一个学生,就需要多个普通变量合起来描述

    • 学生信息:

      • char *name;
      • char *gender;
      • int age;
      • Int score;
    • 要想把这个四个变量合起来此案呢过标识一个学生的信息,首先想到数组,然后数组要求数据类型必须是一致,所以 pass

    • 所以我们想要创建一个新的数据类型,这个数据类型包含了学生的姓名,性别,年龄,成绩四个变量,也就是包含了 char,char,int,int

    使用结构体来创建新的数据类型

    • 1,如何使用结构体来创建新的数据类型呢?
    • 语发格式
        struct Student
        {
            char *name;
            char* gender;
            int age;
            float height;
        }
    // 标识我创建了一个新的数据类型 Student,里面有 char*,char*,int,float,这个数据类型的名称叫做 strct Student
    

声明结构体类型的变量

  • 我们使用结构体仅仅是创建了一个新的类型而已,并没有变量

  • 结构体的作用:是在制定新的数据类型是有那些小变量组合而成

  • 声明结构体类型的变量

    • struct 新类型名称 变量名;
    • struct Student stu
    • 代表声明了一个 struct Student 类型的变量,变量名称叫做 stu
     struct Student stu; // 声明一个 struct Studene 类型的变量 stu
    
    // 这个时候stu 才是一个变量,才会在内存中开辟变量
    
    • 这个变量中,是由这个新的结构体类型规定的小变量组合而成的,
    • 这个结构体大变量是由结构体类型规定的小变量组合而成的
  • 结构体变量的类型:

    • struct Student stu
    • 这个stu 变量的类型是 stuct Studen 而不是 Studen
  • 结构体只是创建了一个新的数据类型,所以你真的要保存 1 个信息的话,还是取声明这个类型的变量

  • 结构体变量里面就是由结构体类型规定的小变量联合而成的

结构体变量的初始化

  • 意义:为结构体变量中的小变量赋值
  • 初始化语法
    • 结构体变量名称.小变量名 = 值;
    • 小变量的名称叫做"成员变量"
  • 结构体变量成员的默认值
    • 声明一个结构体变量如果没有这个结构体变量的成员赋值,那么成员是有值的,是垃圾值
    • 如果部分初始化的话,就跟变量一样,没有被初始化的是 0
    struct Student stu; // 声明一个 struct Studene 类型的变量 stu
    stu.name = "jack"; // 将 jack 赋值给 stu.name
    stu.gender = "男";
    stu.age = 18;
    stu.height = 143.5f;
    
    printf("姓名:%s
性别:%s
年龄:%i
体重:%.2f
",stu.name,stu.gender,stu.age,stu.height);
    
    // 你还能创建一个新的变量,比如 xiaoMing
    struct Student xiaoMing;
    xiaoMing.name = "小明";
    xiaoMing.gender = "男";
    xiaoMing.age = 20;
    xiaoMing.height = 100.4;

什么时候我们需要定义结构体

  • 当我们要存储一个数据,但是发现这个数据是一个大数据,因为这个数据是由其他的小数据联合起来组成的,那么这个时候先试用结构体类自定义这个数据类型是由那些小变量合成的
  • 然后再跟换有这个结构体类型声明变量 来存储数据
  • 比如你要做 ios 界面开发要保存一个图片的大小,那么大小你怎么保存呢?大小是不是有长和宽,那么用数组的话,里面的值是由下标取的你不知道哪个是长,哪个是宽吧.所以可以定义一个新的数据类型
    struct Size{
        int width;
        int height;
    };
    struct Size picture;
    picture.height = 10;
    picture.width = 20;

使用结构体的注意点

  • 一定要先试用结构体定义新的类型,然后才可以根据这个类型声明这个类型的变量
  • 结构体变量也是变量,所以也可以批量声明
struct Student xiaoHua,xiaoLan,xiaoShi; // 批量声明 struct Student 类型的变量xiaoHua,xiaoLan,xiaoShi 
  • 结构体名称的明明规范,要求每一个单词的首字母大写,变量是第一个小写,后面的大写
  • 我们之前是先声明结构体类型,再根据这个类型声明变量,其实可以简化成一步
    // 简化创建类型和声明变量
    struct Computer{
        char* pinpai;
        char* CPU;
        int size;
    }IMac,lenvol,hp,dell;
    lenvol.CPU = "哈哈哈";
    lenvol.pinpai = "联想";
    lenvol.size = 88;
    
  • 匿名结构体

    • 就是这个结构体类型没有名称
    • 只能在创建结构体的时候,创建这个变量.因为不能单独创建变量了
        // 匿名结构体,只能在创建结构体的同时声明这个变量
        struct{
            char* color;
            int size;
        }fengshan;
        fengshan.color = "白色";
        fengshan.size = 20;
    

结构体变量的初始化

  • 第一种方式就是上面 点的方式,但是如果成员变量比较多的时候就会非常麻烦
  • 第二种方式:在声明结构体变量的同时就为结构体变量的成员初始化
    struct Student
    {
        char *name;
        char* gender;
        int age;
        float height;
    };
    struct Student xiaoMing = {"小明","男",18,143.3};
    printf("姓名:%s
性别:%s
年龄:%i
体重:%.2f
",xiaoMing.name,xiaoMing.gender,xiaoMing.age,xiaoMing.height);
  • 第三种初始化:部分初始化,按结构体中的成员变量顺序进行赋值
struct Student
    {
        char *name;
        char* gender;
        int age;
        float height;
    };
struct Student liLei = {"liLei"};
    printf("姓名:%s
性别:%s
年龄:%i
体重:%.2f
",liLei.name,liLei.gender,liLei.age,liLei.height);


// 控制台输出:
姓名:liLei
性别:(null)
年龄:0
体重:0.00
  • 第四种:指定成员变量进行初始化,注意有点
// 指定成员变量进行初始化
    struct Student sss = {.name = "sss",.age = 19,.height = 222.0,.gender = "男"};
    printf("姓名:%s
性别:%s
年龄:%i
体重:%.2f
",sss.name,sss.gender,sss.age,sss.height);

结构体类型的作用域

  • 如果结构体类型是定义在函数内部的,那么这个结构体类型只能在这函数的内部使用
  • 如果我们希望这个结构体类型可以用在所有的函数中,那么就把这个结构体 类型定义在函数的外面,定义在最顶上
  • 一般情况下,我们的结构体类型定义在函数的外面,以便让所有的函数可以使用

结构体变量之间的相互赋值

  • 相同结构体类型之间是可以相互赋值的
  • 结构体变量之间的赋值原理(值传递)
    • 将源结构体变量中的没一个成员的值,拷贝一份赋值给目标结构体变量中的成员
    • 结构体变量之间的赋值是值传递
    // 结构体变量之间的相互赋值
    struct Student {
        char *name;
        char *gender;
        int age;
        float height;
    };
    struct Student xiaoMing;
    xiaoMing.name = "小明";
    xiaoMing.gender = "男";
    xiaoMing.age = 19;
    xiaoMing.height = 173.3;
    
    struct Student xiaoHua = xiaoMing; // 此时是将 xiaoMing 的值
    xiaoHua.name = "小花";
    printf("xiaoHua 的 name 是%s
",xiaoHua.name);
    printf("xiaoHua 的 age 是%i
",xiaoHua.age);
    printf("xiaoHua 的 gender 是%s
",xiaoHua.gender);
    printf("xiaoHua 的 height 是%.2f
",xiaoHua.height);
    printf("xiaoMing 的 name 是%s
",xiaoMing.name);

// 控制台输出
xiaoHua 的 name 是小花
xiaoHua 的 age 是19
xiaoHua 的 gender 是男
xiaoHua 的 height 是173.30
xiaoMing 的 name 是小明

结构体数组

  • 需要保存五个学生的信息
    • 1,声明五个结构体变量,一次存储,这样虽然可以,但是数据非常难以管理
    • 使用结构体数组来保存
  • 声明一个结构体数组
    • 元素类型 数组名[数组长度];
    • struct Student stus[5];
    • 表示我们声明了一个长度为 5 的数组,数组名称叫做 stus,数组的元素的类型是 struct Student
    // 结构体数组
    struct Student {
        char *name;
        int age;
        float score;
    };
    struct Student xiaoMing1 = { "小明1",13,100};
    struct Student xiaoMing2 = { "小明2",14,60};
    struct Student xiaoMing3 = { "小明3",15,10};
    struct Student xiaoMing4 = { "小明4",16,10};
    struct Student xiaoMing5 = { "小明5",17,15};
    
    struct Student stus[5]; // 声明一个struct Studen 类型,长度为 5 的数组,数组名叫 stus
    stus[0] = xiaoMing1;
    stus[1] = xiaoMing2;
    stus[2] = xiaoMing3;
    stus[3] = xiaoMing4;
    stus[4] = xiaoMing5;
    for (int i = 0; i < 5; i++) {
        printf("姓名:%s,年龄:%i,成绩:%2.f
",stus[i].name,stus[i].age,stus[i].score);
    }

结构体数组的初始化

  • 先声明结构体数组,然后用下标 1 个 1 个元素的赋值
  • 注意:当我们为结构体数组的元素赋值的时候,如果直接使用大括号来初始化,就必须前面加小括号,(强转)来告诉编译器我们给的数据类型
    struct Student {
        char *name;
        int age;
        float socre;
    };
    struct Student xiaoMing;
    
    struct Student stus[5] = { // 初始化数组的时候进行赋值,
        {"小明 1",18,99},
        {"小明 2",18,99},
        {"小明 3",18,99},
        {"小明 4",18,99},
        {"小明 5",18,99},
        
    };
    
    for (int i = 0; i < 5; i++) {
        printf("姓名:%s,年龄:%i,成绩:%2.f
",stus[i].name,stus[i].age,stus[i].socre);
    }
  • 在声明结构体数组的同时就为所有元素初始化
    struct Student {
        char *name;
        int age;
        float socre;
    };
    struct Student xiaoMing;
    
    struct Student stus[5] = { // 初始化数组的时候进行赋值,
        {"小明 1",18,99},
        {"小明 2",18,99},
        {"小明 3",18,99},
        {"小明 4",18,99},
        {"小明 5",18,99},
        
    };
    
    for (int i = 0; i < 5; i++) {
        printf("姓名:%s,年龄:%i,成绩:%2.f
",stus[i].name,stus[i].age,stus[i].socre);
    }

结构体数组的长度计算

    // 结构体数组长度的计算
    struct Student {
        char *name; // 8
        int age; // 4
        float score; // 4
    };
    
    struct Student stus[5] = { // 初始化数组的时候进行赋值,
        {"小明 1",18,99},
        {"小明 2",18,99},
        {"小明 3",18,99},
        {"小明 4",18,99},
        {"小明 5",18,99},
        
    };
    int len = sizeof(stus) / sizeof(stus[0]);
    printf("结构体的h长度:%lu
",sizeof(struct Student));
    printf("数组的长度是:%i
",len);

结构体指针

  • 结构体变量,是一个变量

  • struct Studen xiaoMing = {"小明",18,99.5};

  • xiaoMing 首先是一个变量,类型是 struct Student 类型的

  • 既然 xiaoMing 是一个变量,那么这个变量肯定是有地址的,既然有地址那么就可以声明 1 个指针指向这个结构体变量

  • 结构体指针的声明

    • 格式:

    • struct 结构体类型名* 指针名

    • struct Student *pstu,声明了一个 pstu指针变量,这个指针变量的类型是 struct Student 这个指针就只能指向 struct Studen 类型的变量

          struct Student {
              char *name;
              int age;
              float score;
          };
          
          struct Student xiaoMing = {"小明",18,100};
          struct Student *pstu; // 声明一个类型是 struct Studen 类型的指针
      
    • 初始化

    • 1,取出结构体变量的地址,使用取地址符号&

    • 2,将地址赋值给指针变量

        struct Student {
            char *name;
            int age;
            float score;
        };
        
        struct Student xiaoMing = {"小明",18,100};
        struct Student *pstu; // 声明一个类型是 struct Studen 类型的指针
        
        pstu = &xiaoMing; // 取出变量地址赋值给指针 pstu
        struct Student *pstu1 = &xiaoMing; // 声明一个指针并且初始化
    
    • 如何使用指向结构体变量的指针来间接访问这个结构体变量呢?
      • (*结构体指针名).成员变量
      • 结构体指针名->成员变量
        struct Student {
            char *name;
            int age;
            float score;
        };
        
        struct Student xiaoMing = {"小明",18,100};
        struct Student *pstu; // 声明一个类型是 struct Studen 类型的指针
        
        pstu = &xiaoMing;
        struct Student *pstu1 = &xiaoMing;
        // 使用指针进行赋值
        // 第一种
        (*pstu1).name = "李烈"; // 注意要加上括号,*的优先级比较高
        (*pstu1).age = 19;
        pstu1->score = 99.9;
        
    

结构体嵌套

  • 用一个变量保存一个人(姓名,年龄,出生日期,财产)
  • 肯定是要用结构体类型来进行保存
    // 我要定义一个人,有姓名,年龄,出生日期,家产
    
    struct Birthday{
        int year; // 年
        int month; // 月
        int day; // 日
    };
    
    struct Person {
        char *name;
        int age;
        float monney;
        // 出生日期? 出生日期包含年,月,日,三个变量,那我还需要再定义一个结构体来保存出生日期
        struct Birthday birth;
    };

  • 如何初始化呢?
    struct Person xiaoMing = {"小明",18,2.4,{1994,8,8}};
    printf("姓名:%s,年龄:%i,家产:%.2f,出生日期:%i-%i-%i
",xiaoMing.name,xiaoMing.age,xiaoMing.monney,xiaoMing.birth.year,xiaoMing.birth.month,xiaoMing.birth.day);

//控制台输出
姓名:小明,年龄:18,家产:2.40,出生日期:1994-8-8

什么时候会有结构体嵌套

  • 当我们在为结构体定义成员的时候,发现某个成员也是一个大数据,需要其他的几个小类型的数据类型,那么这个时候就可以在定义一个数据类型,来表示这个数据

结构体与函数

  • 结构体作为函数的参数

    • 结构体是我们自定义的一种数据类型,也是一种数据类型,当然也可以作为函数的参数
    • 结构体作为参数传递是值传递
    struct Student {
        char *name;
        int age;
        int score;
    };
    
    int main(){
        struct Student xiaoMing = {"小明",18,99};
        // 定义一个函数,判断学生的成绩大于 60c 输出及格,
        returnJiGe(xiaoMing); // 把 xiaoMing这个变量传递给 returnJiGe 函数,值传递
        return 0;
    }
    
    void returnJiGe(struct Student stu){
        if (stu.score > 60){
            printf("您的成绩及格,请不用担心");
        }else{
            printf("不好意思,你落榜了");
        }
    }
    
    • 如果你就是希望在函数中修改结构体变量的值,传指针就可以了
    struct Student {
        char *name;
        int age;
        int score;
    };
    
    int main(){
        struct Student xiaoMing = {"小明",18,99};
        
        returnJiGe1(&xiaoMing);
        printf("成绩:%i
    ",xiaoMing.score);
        
        return 0;
    }
    
    void returnJiGe1(struct Student stu){
        stu->score = 100; // 修改结构体变量的score 的值
    }
    
  • 结构体作为函数的返回值

    • 结构体类型完全可以作为函数的返回值
    • 再返回的时候直接将这个结构体变量的值返回即可
    • 返回结构体的值
    struct Student {
        char *name;
        int age;
        int score;
    };
    
    struct Student returnZhi();
    
    int main(){
        // 函数返回结构体的值
        struct Student xiaolan = returnZhi();
        printf("姓名:%s,年龄:%i,成绩:%i
    ",xiaolan.name,xiaolan.age,xiaolan.score);
    }
    
    struct Student returnZhi(){
        struct Student p1 = {"小兰",20,44};
        
        return p1;// 返回 p1 的值
    }
    
    • 返回结构体的地址
    struct Student {
        char *name;
        int age;
        int score;
    };
    struct Student *returnDiZhi();
    int main(){
        //返回结构体的地址
        struct Student *pstu = returnDiZhi();
        pstu->name = "你好";
        printf("姓名:%s,年龄:%i,成绩:%i
    ",pstu->name,pstu->age,pstu->score);
    }
    
    // 返回结构体的地址
    struct Student *returnDiZhi(){
        struct Student *p1 = calloc(1, sizeof(struct Student));
        p1->name = "sss";
        p1->age = 88;
        p1->score = 100;
        return p1;
    }
    
原文地址:https://www.cnblogs.com/shanshan-test/p/13126743.html