复合类型 (结构体、联合体和枚举)

复合类型

结构体

一般结构体写在单独的 .h 文件中。

struct 结构体名称
{
    // 结构体成员列表
};

结构体赋值方法

  • 按照结构体顺序赋值

  • 打乱顺序赋值 (不需要遵循结构体顺序)

  • 逐个赋值

  • 定义结构体时添加别名,利用别名赋值

  • 在定义结构体时赋值

  • 示例

    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    // 成员列表
    /*
    struct students {
        char name[20];
        unsigned int age;
        char tel[16];
        float scores[3];
        char sex;
    };
    */
    
    /*
    struct students {
        char name[20];
        unsigned int age;
        char tel[16];
        float scores[3];
        char sex;
    } stu;  // 可以使用别名
    */
    
    // 在定义结构体时赋值
    struct students {
        char name[20];
        unsigned int age;
        char tel[16];
        float scores[3];
        char sex;
    } stu = { "aa", 10, "123456", 30.1, 40.1, 50.1, 'M' };
    
    int main(void)
    {
        // 按结构体顺序赋值
        //struct students   stu = { "aa", 10, "123456", 30.1, 40.1, 50.1, 'M' };
    
        // 打乱顺序赋值
        //struct students   stu = { .sex = 'M', .name = "aa", .age = 10, .tel = "123456", .scores[0] = 30.1, .scores[1] = 40.1, .scores[2] = 50.1 };
    
        // 逐个赋值
        //struct students stu;
        /*
        strcpy(stu.name, "aa");  // 字符串需要通过 strcpy 函数赋值
        stu.age = 10;
        strcpy(stu.tel, "123456");
        stu.scores[0] = 30.1;
        stu.scores[1] = 40.1;
        stu.scores[2] = 50.1;
        stu.sex = 'M';
        */
    
        // 输出
        printf("name: %s, age: %u, tel: %s, scores: %.2f %.2f %.2f, sex: %c\n", stu.name, stu.age, stu.tel, stu.scores[0], stu.scores[1], stu.scores[2], stu.sex);
    
        system("pause");
        return 0;
    }
    

结构体的基本使用

  • 使用 typedef 可以为结构体取别名

  • 不加 typedef 可以直接创建一个结构体变量

  • 结构体声明可以是匿名的

  • 结构体的创建 (栈上/堆上)

  • 结构体变量数组的创建 (栈上/堆上)

  • 示例

    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    // typedef 取别名
    typedef struct Person1
    {
        char name[64];
        int age;
    } myPerson1;
    
    // 不加 typedef 直接创建一个结构体变量
    struct Person2
    {
        char name[64];
        int age;
    } myPerson2 = { "bbb", 24 };  // 结构体变量
    
    // 匿名结构体
    struct
    {
        char name[64];
        int age;
    } myPerson3 = { "bbb", 24 };  // 结构体变量
    
    void test01()
    {
        struct Person2 p1 = { "aaa", 14 };
        struct Person2 p2 = { "ccc", 18 };
        printf("name: %s, age: %d\n", p1.name, p1.age);
        printf("name: %s, age: %d\n", myPerson2.name, myPerson2.age);
    }
    
    // 结构体的创建
    void test02()
    {
        // 创建在栈上
        struct Person2 p1 = { "aaa", 14 };
        printf("name: %s, age: %d\n", p1.name, p1.age);  // name: aaa, age: 14
    
        // 创建在堆上
        struct Person2* p2 = malloc(sizeof(struct Person2));
        strcpy(p2->name, "bbb");
        p2->age = 20;
        printf("name: %s, age: %d\n", p2->name, p2->age);  // name: bbb, age: 20
    
        if (p2 != NULL)
        {
            free(p2);
            p2 = NULL;
        }
    }
    
    // 打印结构体数组
    void printfStruct(struct Person2* persons, int len)
    {
        for (int i = 0; i < len; i++)
        {
            printf("name: %s, age: %d\n", persons[i].name, persons[i].age);
        }
    }
    
    // 结构体变量数组的创建
    void test03()
    {
        // 在栈上分配内存
        struct Person2 persons[] = {
            {"aaa", 10},
            {"bbb", 20},
            {"ccc", 30}
        };
        int len = sizeof(persons) / sizeof(struct Person2);  // 3 行数
        printf("%d %d\n", sizeof(persons), sizeof(struct Person2));  // 204 68
        printfStruct(persons, len);
    
        // 在堆上分配内存
        struct Person2* p2 = malloc(sizeof(struct Person2) * 4);
        for (int i = 0; i < 4; i++)
        {
            sprintf(p2[i].name, "name_%d", i + 1);
            p2[i].age = i + 20;
        }
        printfStruct(p2, 4);
    
        if (p2 != NULL)
        {
            free(p2);
            p2 = NULL;
        }
    }
    
    int main(void)
    {
        test01();
        printf("-------------------\n");
        test02();
        printf("-------------------\n");
        test03();
    
        system("pause");
        return 0;
    }
    

结构体大小和内存结构

  • 结构体大小

    结构体需要根据数据类型做内存对齐。

    • 所有数据类型的大小在内存中存储的地址,一定是它的类型的倍数

    • 数据类型从上到下,按从大到小排列

    • 示意图
      复合类型

    • 示例

      #define _CRT_SECURE_NO_WARNINGS
      #include 
      #include 
      #include 
      
      // 成员列表
      struct students {
          char name[20];
          unsigned int age;
          char tel[16];
          float scores[3];
          char sex;
      } stu;  // 可以使用别名
      
      int main(void)
      {
          // 逐个赋值
          strcpy(stu.name, "aa");  // 字符串需要通过 strcpy 函数赋值
          stu.age = 10;
          strcpy(stu.tel, "123456");
          stu.scores[0] = 30.1;
          stu.scores[1] = 40.1;
          stu.scores[2] = 50.1;
          stu.sex = 'M';
      
          printf("结构体大小为 %d\n", sizeof(stu));  // 结构体大小为 56
          //printf("name: %s, age: %u, tel: %s, scores: %.2f %.2f %.2f, sex: %c\n", stu.name, stu.age, stu.tel, stu.scores[0], stu.scores[1], stu.scores[2], stu.sex);
      
          system("pause");
          return 0;
      }
      
  • 内存对齐
    • 内存对齐原因
      • cpu 将内存当成多个块,每次从内存中读取一个块,这个块的大小可能是 2、4、8、16 (2 的倍数),如果没有内存对齐,获取数据时,需要做二次访问
    • 内存对齐的优缺点
      • 优点
        • 提高访问效率
      • 缺点
        • 以空间换时间
    • 对齐模数

      默认是 8,与平台有关,如果选择 x64,默认是 16。

      #pragma pack(show)  // warning C4810: pragma pack(show) 的值 == 8
      
    • 计算内存对齐
      • 对于标准数据类型,它的地址只要是它长度的整数倍
      • 对于非标准数据类型
        • 普通结构体
          • 第一个属性开始,从 0 开始偏移
          • 第二个属性开始,要放在该类型的大小和对齐模数比,取小的值的整倍数
          • 所有属性都计算完毕后,再整体做二次偏移。将整体计算的结果放在结构体最大类型与对齐模数比,取小的值的整倍数上
        • 结构体嵌套结构体
          • 子结构体放在该结构体中最大类型和对齐模数比的,整数倍上即可
    • 示例
      #define _CRT_SECURE_NO_WARNINGS
      #include 
      #include 
      #pragma pack(show)  // warning C4810: pragma pack(show) 的值 == 8 (如果选择x64,结果是16)
      
      typedef struct _STUDENT1
      {
          int a;    // 0-3
          char b;   // 4-7
          double c; // 8-15
          float d;  //16-23
      } student1;
      
      typedef struct _STUDENT2
      {
          char a;      // 0-7
          student1 b;   // 8-31
          double c;    // 32-39
      } student2;
      
      int main(void)
      {
          printf("sizeof(student1) = %d\n", sizeof(student1));   // sizeof(student1) = 24
          printf("sizeof(student2) = %d\n", sizeof(student2));  // sizeof(student2) = 40
      
          system("pause");
          return 0;
      }
      

结构体数组

  • 示例
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    // 成员列表
    struct students {
        char name[3];
        unsigned int age;
        float scores[3];
    };
    
    int main(void)
    {
        // 结构体数组
        struct students stu[2] = {0};
    
        strcpy(stu[0].name, "aa");  // 字符串需要通过 strcpy 函数赋值
        stu[0].age = 10;
        stu[0].scores[0] = 30.1;
        stu[0].scores[1] = 40.1;
        stu[0].scores[2] = 50.1;
    
        strcpy(stu[1].name, "aa");  // 字符串需要通过 strcpy 函数赋值
        stu[1].age = 11;
        stu[1].scores[0] = 31.1;
        stu[1].scores[1] = 41.1;
        stu[1].scores[2] = 51.1;
    
        // 输出
        for (size_t i = 0; i < 2; i++)
        {
            printf("name: %s, age: %u, scores: %.2f %.2f %.2f\n", stu[i].name, stu[i].age, stu[i].scores[0], stu[i].scores[1], stu[i].scores[2]);
        }
    
        system("pause");
        return 0;
    }
    

结构体排序

  • 结构体成员交换

  • 结构体变量交换

  • 示例

    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct stu {
        char name[3];
        int score[2];
    } s = { 0 };
    
    int main(void)
    {
        struct stu s[2] = { 0 };
    
        // 结构体赋值
        strcpy(s[0].name, "aa");
        s[0].score[0] = 10;
        s[0].score[1] = 30;
    
        strcpy(s[1].name, "bb");
        s[1].score[0] = 11;
        s[1].score[1] = 21;
    
        int n = 2;
        int sum[2] = { 0 };
    
        // 求分数和
        for (size_t i = 0; i < n; i++)
        {
            for (size_t j = 0; j < n; j++)
            {
                sum[i] += s[i].score[j];
            }
        }
    
        //printf("%d %d\n", sum[0], sum[1]);  // 查看求和情况
    
        for (size_t i = 0; i < n; i++)
        {
            for (size_t j = 0; j  sum[j + 1])
                {
                    // 1. 交换变量
                /*  struct stu temp = s[j];
                    s[j] = s[j + 1];
                    s[j + 1] = temp;*/
    
                    // 2. 交换成员
                    // 交换 name
                    char temp1[20] = { 0 };
                    strcpy(temp1, s[j].name);
                    strcpy(s[j].name, s[j + 1].name);
                    strcpy(s[j + 1].name, temp1);
    
                    // 交换 score
                    int temp2[20] = {0};
                    strcpy(temp2, s[j].score);
                    strcpy(s[j].score, s[j + 1].score);
                    strcpy(s[j + 1].score, temp2);
                }
            }
        }
    
        for (size_t i = 0; i < 2; i++)
        {
            printf("%s: %d %d\n", s[i].name, s[i].score[0], s[i].score[1]);
        }
    
        system("pause");
        return 0;
    }
    

结构体和指针

  • 结构体成员为指针
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct stu {
        char* name;
        int age;
    } s;
    
    int main(void)
    {
        //struct stu s;
        s.name = (char*)malloc(sizeof(char) * 3);  // 结构体成员为指针
    
        if (s.name == NULL)
        {
            printf("malloc error:");
            return -1;
        }
    
        strcpy(s.name, "aa");
        s.age = 10;
    
        printf("%s %d\n", s.name, s.age);
    
        free(s.name);
    
        system("pause");
        return 0;
    }
    
  • 结构体指针
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct student {
        char* name;
        int age;
    } stu;
    
    int main(void)
    {
        struct student *s = &stu;
        s->name = (char*)malloc(sizeof(char) * 3);  // 结构体为指针
    
        strcpy(s->name, "aa");
        s->age = 10;
    
        printf("%s %d\n", s->name, s->age);
    
        free(s->name);
    
        system("pause");
        return 0;
    }
    
  • 在堆中开辟结构体
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct student {
        char* name;
        int age;
    } stu;
    
    int main(void)
    {
        struct student *p = (struct student*)malloc(sizeof(stu));  // 在堆中开辟结构体
        p->name = (char*)malloc(sizeof(char) * 3);
    
        strcpy(p->name, "aa");
        p->age = 10;
    
        printf("%s %d\n", p->name, p->age);
    
        free(p->name);
        free(p);
    
        system("pause");
        return 0;
    }
    
  • 在堆中开辟结构体数组
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct student {
        char* name;
        unsigned int *score;
    } stu;
    
    int main(void)
    {
        // 开辟 heap
        struct student* p = (struct student*)malloc(sizeof(stu) * 2);
    
        for (size_t i = 0; i<2; i++)
        {
            p[i].name = (char *)malloc(sizeof(char) * 2);
            p[i].score = (int *)malloc(sizeof(unsigned int) * 3);
    
            // 赋值
            strcpy(p[i].name, "a");
            p[i].score[0] = 10 + i;
            p[i].score[1] = 10 + i;
            p[i].score[2] = 10 + i;
    
            // 输出
            printf("%s %d %d %d\n", p[i].name, p[i].score[0], p[i].score[1], p[i].score[2]);
        }
    
        // 释放 heap
        for (size_t i = 0; i < 2; i++)
        {
            free(p[i].name);
            free(p[i].score);
        }
    
        free(p);
    
        system("pause");
        return 0;
    }
    

结构体和函数

  • 错误用法
    • 结构体做形参
    • 结构体地址做返回值
  • 正确用法
    • 结构体值做形参
    • 结构体作为返回值
  • 示例
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct student {
        char name[3];
        int age;
    };
    
    // 结构体做形参
    void func1(struct student stu)
    {
        strcpy(stu.name, "aa");
        stu.age = 10;
    }
    
    // 结构体值做形参
    void func2(struct student *p)
    {
        strcpy(p->name, "ab");
        p->age = 12;
    }
    
    // 结构体做返回值
    struct student func3(void)
    {
        struct student stu;
        strcpy(stu.name, "ac");
        stu.age = 13;
    
        return stu;
    }
    
    // 结构体地址做返回值
    struct student * func4(void)
    {
        struct student stu;
        strcpy(stu.name, "ad");
        stu.age = 14;
    
        return &stu;
    }
    
    int main(void)
    {
        struct student stu;
        // struct student stu= { .name = "bb", .age = 20 };
        strcpy(stu.name, "bb");
        stu.age = 20;
    
        //func1(stu);   //bb 20
        func2(&stu);  //ab 12
    
        struct student s = func3();
        struct student *p = func4();
    
        printf("%s %d\n", stu.name, stu.age);
        printf("%s %d\n", s.name, s.age);  // ac 13
        printf("%s %d\n", p->name, p->age);  // 乱码,原因是地址记录在栈帧里的,函数调用结束,栈帧释放了
    
        system("pause");
        return 0;
    }
    

结构体的深/浅拷贝

  • 浅拷贝 (系统默认的逐字节拷贝)
    • 系统提供的赋值操作是简单的浅拷贝 (逐字节拷贝)
      • 在栈上没有问题
      • 如果结构体中有属性创建在堆区,就会出现问题,在释放期间,一段内存重复释放,一段内存泄露 -> 分别释放就会造成崩溃
    • 解决方法
      • 深拷贝
  • 深拷贝
    • 自己提供赋值操作 (深拷贝),而不是用系统提供的赋值
  • 示例
    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct Person
    {
        char name[64];
        int age;
    };
    
    // 在栈上的浅拷贝,没有影响
    void test01()
    {
        struct Person p1 = { "aaa", 10 };
        struct Person p2 = { "bbb", 20 };
    
        p1 = p2;
        printf("[p1] name: %s, age: %d\n", p1.name, p1.age);  // [p1] name: bbb, age: 20
        printf("[p2] name: %s, age: %d\n", p2.name, p2.age);  // [p2] name: bbb, age: 20
    }
    
    // 在堆上的浅拷贝,造成内存泄露和重复释放
    void test02()
    {
        struct Person* p1 = malloc(sizeof(struct Person));
        strcpy(p1->name, "aaa");
        p1->age = 10;
        struct Person* p2 = malloc(sizeof(struct Person));
        strcpy(p2->name, "bbb");
        p2->age = 20;
    
        p1 = p2;  // 内存泄露+内存重复
        printf("[p1] name: %s, age: %d\n", p1->name, p1->age);  // [p1] name: bbb, age: 20
        printf("[p2] name: %s, age: %d\n", p2->name, p2->age);  // [p2] name: bbb, age: 20
    
        free(p1);
        //free(p2);  // Error! 由于内存重复,释放时会崩溃
    }
    
    // 解决方案:深拷贝,手动赋值
    void test03()
    {
        struct Person* p1 = malloc(sizeof(struct Person));
        strcpy(p1->name, "aaa");
        p1->age = 10;
        struct Person* p2 = malloc(sizeof(struct Person));
        strcpy(p2->name, "bbb");
        p2->age = 20;
    
        // 先释放原来堆区的内容
        free(p1);  
        p1 = NULL;
    
        //p1 = p2;  // Error!
        // 手动赋值
        p1 = malloc(sizeof(struct Person));  // 原来的内存已经释放了,必须重新申请
        strcpy(p1->name, p2->name);
        p1->age = p2->age;
    
        printf("[p1] name: %s, age: %d\n", p1->name, p1->age);  // [p1] name: bbb, age: 20
        printf("[p2] name: %s, age: %d\n", p2->name, p2->age);  // [p2] name: bbb, age: 20
    
        // 释放内存
        free(p1);
        p1 = NULL;
        free(p2); 
        p2 = NULL;
    }
    
    int main(void)
    {
        test01();
        printf("-------------------\n");
        test02();
        printf("-------------------\n");
        test03();
    
        system("pause");
        return 0;
    }
    

结构体嵌套

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct str1 {
    int a;   // 4
    char b;  // 1
} s1;        // 共 8

struct str2 {
    int aa;   // 4
    char bb;  // 1   int 和 char 共 8,加上 s1 就是 16
    struct str1 s1;  // 结构体嵌套
} s2;

int main(void)
{
    struct str2 s2;
    s2.aa = 22;
    s2.bb = 'b';
    s2.s1.a = 11;
    s2.s1.b = 'd';

    printf("%d\n", s2.s1.a);  // 11
    printf("%d\n", sizeof(s2));  // 16

    system("pause");
    return 0;
}
  • 示意图:内存对齐
    复合类型

  • 结构体的自身引用

    • 结构体可以嵌套另外一个结构体的任何类型变量
    • 结构体不可以嵌套本结构体普通变量
      • 本结构体的类型大小无法确定
      • 类型的本质是固定大小内存块的别名
    • 结构体可以嵌套本结构体指针变量
      • 指针变量的空间能确定,4 字节、8 字节、16 字节、32 字节、64 字节…
  • 结构体嵌套一级指针
    • 示意图:注意创建和释放顺序
      复合类型

    • 示例

      #define _CRT_SECURE_NO_WARNINGS
      #include 
      #include 
      #include 
      
      struct Person
      {
          char* name;
          int age;
      };
      
      // 分配内存
      struct Person** allocatesSpace()
      {
          // 结构体指针数组
          struct Person** temp = malloc(sizeof(struct Person*) * 3);
      
          for (int i = 0; i name = malloc(sizeof(char) * 64);  // 将结构体姓名创建在堆区
              sprintf(temp[i]->name, "name_%d", i + 1);  // 给姓名赋值
              temp[i]->age = i + 20;
          }
      
          return temp;
      }
      
      // 打印结构体内容
      struct Person** printPerson(struct Person** pArray, int len)
      {
          for (int i = 0; i name, pArray[i]->age);
          }
      }
      
      // 释放内存
      struct Person** freeSpace(struct Person** pArray, int len)
      {
          for (int i = 0; i name != NULL)
              {
                  free(pArray[i]->name);
                  pArray[i]->name = NULL;
                  printf("pArray[%d]->name 已释放\n", i);
              }
      
              free(pArray[i]);
              pArray[i] = NULL;
              printf("pArray[%d] 已释放\n", i);
          }
      
          free(pArray);
          pArray = NULL;
          printf("pArray 已释放\n");
      }
      
      int main(void)
      {
          int len = 3;
          struct Person** pArray = NULL;
          pArray = allocatesSpace();  // 分配堆区内存   
          printPerson(pArray, len);  // 打印结构体
          freeSpace(pArray, len);  // 释放内存
      
          system("pause");
          return 0;
      }
      
  • 结构体嵌套二级指针
    • 示意图:层次解析
      复合类型

    • 函数拆分

      • 分配内存
        • 给老师数组分配内存
        • 给每个老师分配内存
        • 给老师姓名分配内存
        • 给学生数组分配内存
        • 给学生姓名分配内存
      • 打印数组

      • 释放内存

        • 老师姓名和学生数组是同级,不分先后。
    • 示例
      #define _CRT_SECURE_NO_WARNINGS
      #include 
      #include 
      #include 
      
      struct Teacher
      {
          char* name;
          char** students;
      };
      
      // 分配内存
      void allocateSpace(struct Teacher*** teachers)
      {
          if (teachers == NULL)
          {
              return;
          }
      
          struct Teacher** ts = malloc(sizeof(struct Teacher*) * 3);
          for (int i = 0; i name = malloc(sizeof(char) * 64);
              sprintf(ts[i]->name, "teacher_%d\n", i + 1);
              printf("%s", ts[i]->name);
      
              // 给学生数组分配内存
              ts[i]->students = malloc(sizeof(char*) * 4);
      
              // 给每个学生姓名分配内存
              for (int j = 0; j students[j] = malloc(sizeof(char) * 64);
                  sprintf(ts[i]->students[j], "teacher_%d-student_%d\n", i + 1, j + 1);
                  printf("%s", ts[i]->students[j]);
              }
          }
          // 传出参数
          *teachers = ts;
      }
      
      // 打印结构体
      void printTeacher(struct Teacher** ts)
      {
          for (int i = 0; i name);
      
              for (int j = 0; j students[j]);
              }
          }
      }
      
      // 释放内存
      void freeTeacher(struct Teacher** ts)
      {
          if (ts == NULL)
          {
              return;
          }
      
          for (int i = 0; i < 3; i++)
          {
              // 释放学生姓名
              for (int j = 0; j students[j]);
              }
      
              // 释放学生数组
              free(ts[i]->students);
              ts[i]->students = NULL;
      
              // 释放老师姓名
              free(ts[i]->name);
              ts[i]->name = NULL;
      
              // 释放老师
              free(ts[i]);
              ts[i] = NULL;
          }
      
          // 释放结构体指针
          free(ts);
          ts = NULL;
      }
      
      int main(void)
      {
          struct Teacher** pArray = NULL;
          allocateSpace(&pArray);  // 开辟内存
          printf("--------------------------\n");
          printTeacher(pArray);  // 打印数组
          freeTeacher(pArray);  // 释放内存
      
          system("pause");
          return 0;
      }
      

结构体偏移量

通过偏移量可以操作内存。

  • 求偏移量
    • offsetof 函数
  • 对于嵌套结构体

  • 示例

    #define _CRT_SECURE_NO_WARNINGS
    #include 
    #include 
    #include 
    
    struct Teacher
    {
        char a;
        int b;
    };
    
    // 求偏移量
    void test01()
    {
        struct Teacher t;
        struct Teacher* p = &t;
    
        printf("a 的偏移量 = %d\n", &(p->b) - p);            // a 的偏移量 = 1
        printf("b 的偏移量 = %d\n", (int)&(p->b) - (int)p);  // b 的偏移量 = 4 (offsetof 的底层实现方式)
        printf("结构体 Teacher 的偏移量 = %d\n", offsetof(struct Teacher, b));  // 结构体 Teacher 的偏移量 = 4
    }
    
    // 通过偏移量操作内存
    void test02()
    {
        struct Teacher t = { 'a', 10 };
    
        printf("t.b = %d\n", *((char*)&t + offsetof(struct Teacher, b)));  // t.b = 10
        printf("t.b = %d\n", *(int*)((char*)&t + offsetof(struct Teacher, b)));  // t.b = 10
        printf("t.b = %d\n", *(int*)((int*)&t + 1));  // t.b = 10
    }
    
    struct TeacherNest  // 结构体嵌套结构体
    {
        char a;
        int b;
        struct Teacher c;
    };
    
    void test03()
    {
        struct TeacherNest t = { 'a', 10, 'b', 20 };
        printf("sizeof(struct TeacherNest) = %d\n", sizeof(struct TeacherNest));  // sizeof(struct TeacherNest) = 16
    
        int offset1 = offsetof(struct TeacherNest, c); 
        int offset2 = offsetof(struct Teacher, b); 
        printf("offset1 = %d, offset2 = %d\n", offset1, offset2);  // offset1 = 8, offset2 = 4
        printf("t.c.b = %d\n", *(int*)((char*)&t + offset1 + offset2));  // t.c.b = 20
        printf("t.c.b = %d\n", t.c.b);  // t.c.b = 20
    
        printf("b = %c\n", *((char*)&t + offset1));  // b = b
        printf("%d\n", ((struct Teacher*)((char*)&t + offset1))->b);  // 20
    }
    
    int main(void)
    {
        test01();
        printf("------------------\n");
        test02();
        printf("------------------\n");
        test03();
    
        system("pause");
        return 0;
    }
    

共用体 (联合体)

成员地址

  • 内部所有成员变量地址一致,等同于整个联合体的地址
  • 修改其中任意一个成员变量的值,其他成员变量也会随之修改

联合体的大小

  • 是内部成员变量中,最大的那个成员变量的大小
  • 对齐

示例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef union test {
    char ch;
    short sh;
    int a;
} test_t;

int main(void)
{
    test_t obj;
    obj.a = 0x123413241;  // 更改后,所有地址都变为 009FF88C

    printf("&obj    = %p\n", &obj);  // 更改前,所有地址都是 00BDF834
    printf("&obj.ch = %p\n", &obj.ch);  // & obj.ch = 00BDF834
    printf("&obj.sh = %p\n", &obj.sh);  // & obj.sh = 00BDF834
    printf("&obj.a  = %p\n", &obj.a);  // & obj.sh = 00BDF834

    system("pause");
    return 0;
}

枚举

枚举常量

// enum color { 枚举常量 };
enum color { red, green, blue, pink, yellow};
  • 是整型常量,不能是浮点数,可以是负数
  • 默认初值从 0 开始,后续常量较前一个常量 +1
  • 可以给任意一个常量赋任意一个初值 (后续常量较前一个常量 +1)

示例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

enum color1 {red, green, blue, pink, yellow};
          // 0     1      2     3     4   // 原始位置
enum color2 { big, small = 10, medium };

int main(void)
{
    printf("%d %d %d %d %d\n", red, green, blue, pink, yellow);  // 0 1 2 3 4

    int blue = 10;
    printf("%d %d %d %d %d\n", red, green, blue, pink, yellow);  // 0 1 10 3 4
    printf("%d %d %d\n", big, small, medium);  // 0 10 11

    system("pause");
    return 0;
}
英仔
版权声明:本站原创文章,由 英仔 2022-08-12发表,共计13979字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)