当前位置: 首页 > news >正文

C语言基础18

内容提要

  • 构造类型

    • 结构体

    • 共用体/联合体

    • 枚举

    • typedef

构造类型

数据类型

  1. 基本类型/基础类型

    • 整型

      • 短整型:short [int] -- 2字节

      • 基本整型:int -- 4字节

      • 长整型:long [int] -- 32位4字节/64位8字节

      • 长长整型:long long [int] -- 8字节(大多数现代机器,旧机器可能超过8字节),C99新增

        注意:以上类型又都分为signed(默认)和unsigned

    • 浮点型

      • 单精度:float -- 4字节

      • 双精度:double -- 8字节

      • 长双精度:long double -- C99新增

    • 字符型:char -- 1字节

  2. 指针类型

    • 数据类型*:int* char* float*等 -- 8字节

    • void*:通用类型指针(万能指针) -- 8字节

  3. 空值类型

    • void:无返回值,无形参(不能定义变量)

  4. 构造类型(自定义类型)

    • 结构体类型:struct

    • 共用体/联合体类型:union

    • 枚举类型:enum

结构体

结构体的定义【定义类型】
  • 定义:自定义数据类型的一种,关键字struct,结构体类型的变量可以存储多个不同数据类型的数据。

  • 语法:

     struct 结构体名   // 我们定义的数据类型的名字
     {
         数据类型1 成员名称1; // 结构体中的变量叫做成员
         数据类型2 成员名称2; 
         .....
     };

    注意:结构体中定义的变量,称之为成员变量(成员属性)

  • 格式说明:

    • 结构体名:合法的标识符,建议首字母大写(所谓的结构体名,就是自定义类型的类型名称)

    • 数据类型n:C语言支持的所有类型(包括函数,函数在这里用函数指针表示)

    • 成员名称n:合法的标识符,就是变量的命名标准

    • 数据类型n成员名称n:类似于定义变量,定义了结构体中的成员

  • 注意:

    • 结构体在定义的时候,成员不能赋值,举例:

       struct Cat
       {
           int age = 5;       // 错误,结构体定义的时候,成员不能赋值
           double height;     // 正确
           void (*run)(void); // 正确
       };
  • 常见的定义格式:

    • 方式1:常规定义(命名结构体,只定义数据类型) -- 推荐

       struct Student
       {
           int num;            // 学号
           char name[20];      // 姓名
           char sex;           // 性别
           int age;            // 年龄
           char address[100];  // 籍贯
           void (*info)(void); // 信息输出(函数指针)
       };
    • 方式2:定义匿名结构体(常用于作为其他结构体的成员使用)

       struct Dog
       {
           char *name;   // 姓名
           int age;      // 年龄
           // 匿名结构体
           struct
           {
               // 匿名结构体定义时不能省略成员
               int year; // 年
               int month;// 月
               int day;  // 日
           } birthday;   // 生日,匿名结构体作为其他结构体成员时,需添加成员名称,否则无法访问
       };

      注意:定义匿名结构体的同时必须定义结构体成员,否则编译报错;结构体可以作为另一个结构体的成员

      总结:

      • 结构体可以定义在局部位置,也可以定义在全局位置(用的比较多,因为可以实现复用)

      • 全局位置的结构体名和局部位置的结构体名可以相同,遵循就近原则(和变量的定义同理)

  • 结构体类型的使用:

    利用结构体类型定义变量、定义数组,也可以作为函数的返回值和参数;结构体类型的使用与基本数据类型的使用类似。

结构体变量的定义【定义变量】
  • 三种形式定义结构体变量

    结构体变量也称之为结构体实例

    • 第1种:

      ①先定义结构体(定义数据类型)

      ②然后使用(使用数据类型定义变量)

      举例:

       // 定义结构体(定义数据类型)
       struct A
       {
           int a;
           char b;
       };
       ​
       // 定义结构体变量
       struct A x; // struct A就是数据类型,x就是变量名
       struct A y; // struct A就是数据类型,y就是变量名
    • 第2种:

      ①在定义结构体的同时,定义结构体变量

      语法:

       struct 结构体名
       {
           数据类型1 数据成员1;
           ...
       } 变量列表;

      举例:

       struct A
       {
           int a;
           char b;
       } x,y;

      此时定义了一个结构体A,x和y是这个结构体类型的变量

    • 第3种:(不推荐)

      在定义匿名结构体的同时,定义结构体变量

       struct 
       {
           int a;
           char b;
       } x,y;
       ​
       struct
       {
           int a;
           char b;
       } z;
       ​

      此时定义了一个没有名字的结构体(匿名结构体);x,y是这个结构体类型的变量

  • 匿名结构体

    • 优点:少写一个结构体名称

    • 缺点:只能使用一次,定义结构体类型的同时必须定义变量

    • 应用场景:

      • 当结构体的类型只需要使用一次,并且定义类型的同时定义变量

      • 作为其他结构体的成员使用

    • 定义结构体的同时,定义结构体变量初始化

      struct Cat
      {
          int age;
          char color[20];
      } cat;
      • 结构体成员部分初始化时,大括号{}不能省略

      • 结构体成员,没有默认值,是随机值

  • 案例

    /**
     * 先定义结构体,再定义结构体变量
     */
    void fun1()
    {
        // 定义结构体
        struct A
        {
            int a;
            char b;
        };
        
        // 定义结构体变量/实例
        struct A x;
        struct A y;
    }
    
    /**
     * 定义结构体的同时,定义结构体变量
     */
    void fun2()
    {
        struct A
        {
            int a;
            char b;
        } x,y;
        
        struct A z;
    }
    
    /**
     * 定义匿名结构体的同时定义变量
     */
    void fun3()
    {
        struct
        {
            int a; // 结构体成员
            char b;
        } x,y;
        
        struct
        {
            int a;
            char b;
        } z;
    }
    
    int main(int argc, char *argv[])
    {
        fun1();
        fun2();
        fun3();
        
        return 0;
    }
结构体变量的使用
  • 结构体变量访问结构体成员

    • 语法:

      结构体变量名.成员名;

      ①可以通过访问给成员赋值(存数据)

      ②可以通过访问获取成员的值(取数据)

    • 结构体变量未初始化,结构体的成员是随机值(和普通的变量、数组同理)

  • 结构体变量在定义时,可以初始化

    • 建议用大括号{}标明数据的范围

    • 结构体成员初始化,可以部分初始化(和数组类似),部分初始化时一定要带大括号标明数据的范围

  • 案例:

    /* 定义全局的结构体 */
    struct Dog
    {
        char *name;        // 名字
        int age;           // 年龄
        char sex;          // M:公,W:母
        void (*eat)(void); // 吃狗粮
    };
    
    void eat()
    {
        printf("狗狗在吃狗粮!\n");
    }
    
    /**
     * 方式1:先定义,再初始化
     */
    void fun1()
    {
        // 定义结构体变量
        struct Dog dog;
        
        // 给结构体变量成员赋值
        dog.name = "旺财";
        dog.age = 5;
        dog.sex = 'M';
        dog.eat = eat; // 函数指针,eat指针指向eat函数
        
        // 访问结构体变量成员
        printf("%s,%d,%c\n",dog.name, dog.age, dog.sex);
        // 访问成员方法(函数)
        dog.eat();
    }
    
    /**
     * 方式2:定义的同时初始化
     */
    void fun2()
    {
        // 定义结构体变量的同时,给变量成员初始化
        struct Dog dog = {"旺财",5,'M',eat};
        // 修改成员的值
        dog.name = "金宝";
        
        // 访问结构体变量成员
        printf("%s,%d,%c\n",dog.name, dog.age, dog.sex);
        // 访问成员方法(函数)
        dog.eat();
    }
    
    
    int main(int argc, char* argv[])
    {
        fun1();
        fun2();
        return 0;
    }
结构体数组的定义
  • 什么时候需要结构体数组

    比如:我们需要管理一个学生对象,只需要定义一个struct Student yixin;

    假如:我们需要管理一个班的学生对象,此时就需要定义一个结构体数组struct Student stus[33];

  • 四种形式定义结构体数组

    • 第1种:结构体 → 结构体变量 → 结构体数组,举例:

      // 定义一个学生结构体(定义数据类型)
      struct Student
      {
          char* name;      // 姓名
          int age;         // 年龄
          float scores[3]; // 三门课程的成绩
      };
      
      // 定义结构体变量/实例
      struct Student zhangsan = {"张三",21,{89,99,78}};
      struct Student lisi = {"李四",22,{66,78,98}};
      
      // 定义结构体数组
      struct Student stus[3] = {zhangsan,lisi};
    • 第2种:结构体 → 结构体数组,举例:

      // 定义一个学生结构体(定义数据类型)
      struct Student
      {
          char* name;      // 姓名
          int age;         // 年龄
          float scores[3]; // 三门课程的成绩
      };
      
      // 定义结构体数组并初始化
      struct Student stus[3] = { 
          {"张三",21,{89,99,78}},
          {"李四",22,{66,78,98}} 
      };
    • 第3种:结构体、结构体数组一体化(含初始化),举例:

      // 定义一个学生结构体(定义数据类型)
      struct Student
      {
          char* name;      // 姓名
          int age;         // 年龄
          float scores[3]; // 三门课程的成绩
      } stus[3] = { 
          {"张三",21,{89,99,78}},
          {"李四",22,{66,78,98}} 
      };
    • 第4种:结构体、结构体数组一体化(不含初始化),举例:

      // 定义一个学生结构体(定义数据类型)
      struct Student
      {
          char* name;      // 姓名
          int age;         // 年龄
          float scores[3]; // 三门课程的成绩
      } stus[3];
      
      // 赋值
      stus[0].name = "张三";
      stus[0].age = 21;
      stus[0].scores[0] = 89;
      stus[0].scores[1] = 99;
      stus[0].scores[2] = 78;
      
      stus[1].name = "李四";
      stus[1].age = 22;
      stus[1].scores[0] = 66;
      stus[1].scores[1] = 78;
      stus[1].scores[2] = 98;
结构体数组的访问

语法:

结构体指针 -> 成员名

举例:

// 普通指针访问
(*p).成员名
// 结构体指针访问(结构体指针访问符:->) 等价于上面写法
p -> 成员名

案例:

/* 定义全局Student结构体 */
struct Student
{
    int id;
    char *name;
    int age;
    float scores[3];
    void (*info)(char*,int);
};

/**
 * 定义一个函数,输出信息
 */
void info(char* name,int age)
{
    printf("大家好,我是%s,今年%d岁了!\n",name,age);
}

void func()
{
    // 定义结构体实例并初始化
    struct Student stu1 = {1,"张三",21,{78,88,98},info};
    struct Student stu2 = {2,"李四",22,{90,98,78},info};
    // stu1.info = info;
    // stu2.info = info;
    
    // 定义结构体数组并初始化
    struct Student stus[] = {stu1,stu2};
    
    // 遍历数组
    // 计算len
    int len = sizeof(stus)/sizeof(stus[0]);
    
    // 指针遍历
    // 定义一个指针,获取遍历的学生对象
    struct Student *p = stus;
    for(; p < stus + len; p++)
    {
        printf("%d,%s,%d,%.2f,%.2f,%.2f\n",p->id,p->name,p->age,p->scores[0],p->scores[1],p->scores[2]);
        // 调用函数 p-> info 等价于 (*p).info();
        p -> info(p -> name, p -> age);
    }
    printf("\n");
}

int main(int argc, char* argv[])
{
    func();
    return 0;
}

注意:

->[]共存的时候,它们的优先级关系:[]>->

结构体类型

结构体数组

案例

  • 需求:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人名字,要求最后输出各人得票的结果

  • 代码:

    #include <string.h>
    
    /**
     * 定义一个候选人结构体
     */
    struct Person
    {
        char name[20]; // 候选人名字
        int count;     // 候选人票数
    };
    
    /**
     * 定义候选人数组,并初始化
     */
    struct Person persons[3] = {
        {"诸葛蛋蛋",0},
        {"司马苟丹",0},
        {"上官铁祝",0},
    };
    
    int main(int argc, char *argv[])
    {
        // 定义循环变量
        int i,j;
        
        // 创建一个char数组,用来接收控制台输入的候选人的名字
        char leader_name[20];
        
        // 使用一个for循环,模拟10次投票
        for(i = 0; i < 10; i++)
        {
            printf("请输入您要投票的候选人姓名:\n");
            scanf("%s",leader_name);
            
            // 给被投票的候选人+1票
            for(j = 0; j < 3; j++)
            {
                // 判断两个字符串的结果是否相同
                if(strcmp(leader_name,persons[j].name) == 0)
                {
                    persons[j].count++;
                }
            }
        }
        
        printf("\n投票结果:\n");
        
        // 方式1
        struct Person *p = persons; // p就是结构体指针
        for(; p < persons + 3; p++)
        {
            // printf("  %s:%d\n",(*p).name, (*p).count);
            printf("  %s:%d\n",p -> name, p -> count);
        }
        printf("\n");
        
        // 方式2
        for(i = 0; i < 3; i++)
        {
            printf("  %s:%d\n",(persons+i) -> name, (persons+i) -> count);
        }
        
        return 0;
    }
结构体指针
  • 定义:结构体类型的指针变量指向结构体变量或者数组的起始地址

  • 语法:

    struct 结构体名 *指针变量列表;
  • 举例:

    struct Dog
    {
        char name[20];
        int age;
    };
    
    struct Dog dog = {"苟富贵",5};
    
    // 基于结构体的构造体指针
    struct Dog *p = &dog;
结构体成员的访问
  • 结构体成员访问

    • 结构体数组名访问结构体成员

      • 语法:

        结构体数组名 -> 成员名;  // 等价于如下写法
        (*结构体数组名).成员名;
      • 举例:

        printf("%s:%d\n",persons -> name, persons -> count);
    • 结构体成员访问符

      • .:左侧是结构体变量,也可以叫做结构体对象访问成员符,右侧是结构体成员

      • ->:左侧是结构体指针,也可以叫做结构体指针访问成员符,右侧是结构体成员

      • 举例:

        struct Person *p = persons; // p 就是构造体指针
        for(; p < persons + 3; p++)
            printf("%s:%d\n",p -> name, p -> count);
    • 访问结构体成员有两种类型,三种方式:

      • 类型1:通过结构体变量(对象|实例)访问成员

        struct Stu
        {
            int id; // 结构体成员
            char name[20];
        } stu; // 结构体变量
        
        // 访问成员
        stu.name;
      • 类型2:通过结构体指针访问成员

        • 第1种:指针引用访问成员

          struct Stu
          {
              int id; // 结构体成员
              char name[20];
          } stu; // 结构体变量 | 对象 | 实例
          
          struct Stu *p = &stu;
          // 指针引用访问成员
          p -> name; // 等价于(*p).name
        • 第2种:指针解引用间接访问成员

          struct Stu
          {
              int id; // 结构体成员
              char name[20];
          } stu; // 结构体变量 | 对象 | 实例
          
          struct Stu *p = &stu;
          // 指针解引用访问成员
          (*p).name; // 等价于 p -> name
      • 结构体数组中元素的访问

        struct Stu
        {
            int id;
            char name[20];
            float scores[3];
        } stus[3] = {
            {1,"张三",{67,77,88}},// (stus+1)
            {2,"李四",{90,99,98}},
            {3,"王五",{88,97,77}}
        };
        // 取数据 -- 下标法
        printf("%s,%.2f\n",stus[1].name,stus[1].scores[1]); // 李四 99
        
        // 取数据 -- 指针法(->)
        printf("%s,%.2f\n",stus -> name,stus -> scores[2]); // 张三 88
        printf("%s,%.2f\n",(stus+1) -> name,(stus+1) -> scores[2]); // 李四 98
        printf("%s,%.2f\n",(*(stus+1)).name,(*(stus+1)).scores[2]); // 李四 98

        小贴士:

        结构体是自定义数据类型,它是数据类型,用法类似于基本类型的int

        结构体数组它是存放结构体对象的数组,类似于int数组存放int数据

        基本类型数组怎么用,结构体数组就怎么用--->可以遍历,可以作为形式参数,也可以做指针等

  • 结构体类型的使用案例

    结构体可以作为函数的返回类型、形式参数等

    举例:

    #include <string.h>
    
    /**
     * 定义结构体
     */
    struct Cat
    {
        char *name;     // 姓名
        int age;        // 年龄
        char color[20]; // 颜色
    };
    
    /**
     * 结构体类型作为形式参数
     */
    void test1(struct Cat c)
    {
        printf("test1:\n%s,%d,%s\n",c.name, c.age, c.color);
    }
    
    /**
     * 结构体类型作为形式参数,结构体类型作为返回值类型
     */
    struct Cat test2(struct Cat c)
    {
        return c;
    }
    
    /**
     * 结构体数组作为形式参数
     */
    struct Cat test3(struct Cat cats[], int len)
    {
        struct Cat *p = cats;
        for(; p < cats + len; p++)
        {
            printf("test3:\n%s,%d,%s\n",p -> name, p -> age, p -> color);
        }
    }
    
    /**
    * 结构体指针作为形式参数,结构体指针作为返回类型
    */
    struct Cat *test4(struct Cat *cats, char *name)
    {
        struct Cat *p = cats;
        
        // 遍历
        for(; p < cats + 3; p++)
        {
            if(strcmp(name, p -> name) == 0) return p;
        }
        
        return NULL;// 指针返回空
    }
    
    struct Cat *test5(struct Cat (*cats)[3], char *name) 
    {
        //数组指针遍历:用指针偏移
        for(int i = 0; i < 3; i++)
        {
            if(strcmp(name, (*cats)[i].name) == 0)
                return &(*cats)[i];     //*cats是整个数组的指针
        }
        return NULL;
    }
    
    int main(int argc,char *argv[])
    {
    
        // 定义结构体对象
        struct Cat cat = {"狗狗", 8, "yellow"};
        
        // 结构体对象作为实际参数
        test1(cat);
        
        // 结构体作为函数参数和返回值类型
        struct Cat res_cat = test2(cat);
        printf("test2:\n%s,%d,%s\n",res_cat.name, res_cat.age, res_cat.color);
        
        // 定义结构体数组
        struct Cat cats[] = {
            {"汤姆",16,"blue"},
            {"杰瑞",18,"green"},
            {"索菲",19,"red"}
        };
        // 结构体作为函数的实际参数
        test3(cats,3);
        
        // 结构体指针作为形式参数,结构体指针作为返回类型
        struct Cat *p = test4(cats,"汤姆");
        printf("test4:\n%s,%d,%s\n", p -> name, p -> age, p -> color);
        
        struct Cat *q = test5(&cats, "汤姆");
    	printf("test5:\n%s,%d,%s\n", q -> name, q -> age, q -> color);
        
        return 0;
    }
结构体类型求大小
字节对齐
  • 字节对齐的原因:

    1. 硬件要求:某些硬件平台(如ARM、x86)要求特定类型的数据必须对齐到特定地址,否则会引发性能下降或硬件异常

    2. 优化性能:对齐的数据访问速度更快。例如,CPU访问对齐的int数据只需一次内存操作,而未对齐的数据可能需要多次操作

  • 字节对齐规则:

    1. 默认对齐规则

      • 结构体的每个成员按其类型大小和编译器默认对齐数(通常是类型的自然对齐数)对齐

      • 结构体的总大小必须是最大对齐数的整数倍

    2. 对齐细节

      • 基本类型的对齐数char(1字节)、short(2字节)、int(4字节)、double(8字节)

      • 结构体成员的对齐:每个成员的起始地址必须是对齐数的整数倍

      • 结构体总大小的对齐:结构体的总大小必须是其最大对齐数的整数倍

    3. #pragma pack(n)的影响:使用#pragma pack(n)可以强制指定对齐数为nn为 1、2、4、8、16)。此时:

      • 每个成员的对齐数取n和其类型大小的较小值

      • 结构体的总大小必须是n和最大对齐数中的较小值的整数倍

  • 对齐示例

    • 默认对齐

      struct S1
      {
      	char c;   // 1字节(偏移0)
          int i;    // 4字节(需对齐到4,填充3字节,偏移4-7)
          double d; // 8字节(需对齐到8,偏移8-15)
      };
      struct S1
      {   
          double d; // 8字节(偏移0-7)
          int i;    // 4字节(需对齐到8,偏移8-11)
          char c;   // 1字节(需对齐到12,还需填充3字节)
      };
      struct S1
      {
      	char c;   // 1字节(偏移0,填充7字节)
          double d; // 8字节(需对齐到8)
          int i;    // 4字节(需对齐到16,填充4字节)
      };

      总结:结构体中,成员的顺序会影响到结构体最终的大小

  • 使用#pragma pack(1)

    #pragma pack(1)
    struct S1
    {
    	char c;   // 1字节(偏移0)
        int i;    // 4字节(偏移1-4)
        double d; // 8字节(偏移5-12)
    };
    #pragma pack()
    // S1 的大小为 13字节
    #pragma pack(2)
    struct S1
    {
    	char c;   // 1字节(偏移0,填充1字节)
        int i;    // 4字节(偏移2-5)
        double d; // 8字节(偏移6-13)
    };
    #pragma pack()
    // S1 的大小为 14字节
    #pragma pack(4)
    struct S1
    {
    	char c;   // 1字节(偏移0,填充3字节)
        int i;    // 4字节(偏移4-7)
        double d; // 8字节(偏移8-15)
    };
    #pragma pack()
    // S1 的大小为 16字节
  • 在GNU标准中,可以在定义结构体时,指定对齐规则:

    __attribute__((packed)); // 结构体所占内存大小是所有成员所占内存大小之和
    ——attribute__((aligned(n))); // 设置结构体占n个字节,如果n比默认值小,n不起作用;n必须是2的次方

    案例:

    int main(int argc,char *argv[])
    {
        struct Cat
        {
            char sex __attribute((aligned(2)));// 1 -- 2
            int id;// 4
            char name[20];// 20
        } __attribute__((packed)); // 结构体所占内存大小是所有成员所占内存大小之和,packed---结构体取消所有填充
        printf("%ld\n",sizeof(struct Cat));// 默认字节对齐(28)/ 使用packed后(25)
        return 0;
    }
    
    // 编译结果是26:存在隐藏行为
    // 1. aligned(2) 覆盖结构体对齐:GCC 可能将结构体的整体对齐要求设为 max(2, 1) = 2(忽略 id 的 4 字节对齐,因 packed 冲突)。
    // 2. 结构体填充规则:总大小需为 2 的倍数 → 25 → 26 字节    
柔性数组

定义:柔性数组不占有结构体的大小

语法:

struct St
{
    ...
    char arr[0];
};

案例:

int main(int argc,char *argv[])
{
    struct Cat
    {
        char sex __attribute((aligned(2)));// 1 -- 2
        int id; // 4
        char arr[0]; // 0 柔性数组不占用结构体的大小
        char name[20]; // 20
    } __attribute__((packed)); // 结构体所占内存大小是所有成员所占内存大小之和
    printf("%ld\n",sizeof(struct Cat));// 默认字节对齐(28)/ 使用packed后(25)/ 使用aligned之后(26)
        return 0;
}
课堂练习

计算以下结构体的大小

// 定义测试结构体
struct TEST1
{
    char a;
    int b;
};

struct TEST1_1
{
    char a;
    int b;
}__attribute__((packed));// 取消字节对齐,取消之后,结构体数据类型大小就等于其所有成员的数据类型之和

struct TEST1_2
{
    char a __attribute__((aligned(2)));
    int b;
};

struct TEST2
{
    char a;
    short c;
    int b;
};

struct TEST3
{
    int num;
    char name[10];
    char sex;
    int age;
    double score;
};

struct TEST3_1
{
    int num;
    char name[10];
    char sex;
    double score;
    int age;
};

struct TEST4
{
    int num;
    short name[5];
    char sex;
    int age;
    int scores[2];
};

int main(int argc,char *argv[])
{
    // 创建结构体变量
    struct TEST1 test1;
    struct TEST2 test2;
    struct TEST3 test3;
    struct TEST3_1 test3_1;
    struct TEST4 test4;
    struct TEST1_1 test1_1;
    struct TEST1_2 test1_2;
    
    // 计算大小
    printf("%lu\n",sizeof(test1));// 8
    printf("%lu\n",sizeof(test2));// 8
    printf("%lu\n",sizeof(test3));// 32
    printf("%lu\n",sizeof(test3_1));// 32
    printf("%lu\n",sizeof(test4));// 28
    printf("%lu\n",sizeof(test1_1));// 5
    printf("%lu\n",sizeof(test1_2));// 8
    return 0;
}

共用体/联合体类型

  • 定义:使几个不同变量占用同一段内存的结构。共用体按定义中需要存储空间最大的成员来分配存储单元,其他成员也是使用该空间,它们的首地址是相同的

  • 定义格式:

    union 共用体名称
    {
        数据类型 成员名;
        数据类型 成员名;
        ...
    };
  • 共用体的定义和结构体类似

    • 可以有名字,也可以匿名

    • 共用体在定义时也可以定义共用体变量

    • 共用体在定义时也可以初始化成员

    • 共用体也可以作为形参和返回值类型使用

    • 共用体也可以定义共用体变量

    • ...

      也就是说,结构体的用法,共用体都支持

  • 注意:

    • 共用体弊大于利,尽量少用,一般很少用;

    • 共用体变量在某一时刻只能存储一个数据,并且也只能取出一个数

    • 共用体所有成员共享同一内存空间,同一时间只能存储一个值,可能导致数据覆盖

    • 共用体和结构体都是自定义数据类型,用法类似于基本数据类型

      • 共用体可以是共用体的成员,也可以是结构体的成员

      • 结构体可以是结构体的成员,也可以是共用体的成员

  • 案例:

    // 定义共用体
    union S
    {
        char a;
        float b;
        int c;
    };// S的大小是4字节
    
    // 共用体作为共用体成员
    union F
    {
        char a;
        union S s; // 4字节
    };// 大小是4字节
    
    // 共用体作为结构体的成员
    struct G
    {
        int a;
        union S s;
    };// 大小是8字节
    
    // 定义一个结构体
    struct H
    {
        int a;
        char b;
    };// 大小是8字节
    
    // 结构体作为结构体成员
    struct I
    {
    
        int a;
        int b;
        struct H h;
    };// 大小是16字节
    
    // 共用体作为结构体成员
    struct J
    {
        int a;// 4
        char b;// 1
        union S s;// 4
    };// 大小是12字节
    
    void test1()
    {
        // 定义一个共用体
        union Stu
        {
            int num;
            char sex;
            double score;
        };
        
        // 定义匿名共用体
        union
        {
            int a;
            char c;
        } c;// 匿名共用体,一定要定义出变量名,否则报错
        
        printf("%lu,%lu\n",sizeof(union Stu),sizeof(c));
    }
    
    void test2()
    {
        union C
        {
            int a;
            char b;
        };
        
        // 定义变量
        union C c;
        // 存储数据
        c.a = 10;
        c.b = 'A';
    
        printf("%d---%d\n",c.a,c.b); // 65---65
        c.a += 5; // c.a = 65 + 5
        printf("%d---%d\n",c.a,c.b); // 70---70
        
        union E
        {
            char *f;// 8 存储的是 hello world! 的首地址
            long a;// 8
            int b;// 4
        } e = {"hello world!"}; // hello world存储在数据段
        
        printf("%s,%ld,%d\n",e.f,e.a,e.b);
    }
    int main(int argc,char *argv[])
    {
        test1();
        test2();
        
        return 0;
    }

相关文章:

  • 关于可变形卷积
  • STM32单片机入门学习——第24节: [8-2]DMA数据转运DMA+AD多通道
  • Debian 12 服务器搭建Beego环境
  • 汽车BMS技术分享及其HIL测试方案
  • 计算机网络-子网划分试题七
  • PyCharm2024.3.5专业版解决Conda executable is not found问题
  • 信息学奥赛一本通 1929:【04NOIP普及组】火星人 | 洛谷 P1088 [NOIP 2004 普及组] 火星人
  • MIME类型
  • Android中的libs.versions.toml文件
  • Sentinel核心源码分析(下)
  • Python----PaddlePaddle(深度学习框架PaddlePaddle,概述,安装,衍生工具)
  • 红宝书第三十一讲:通俗易懂的包管理器指南:npm 与 Yarn
  • 武汉迅狐科技:AI赋能企业营销,打造智能获客新范式
  • 【软件系统架构】分布式架构
  • 六种蝴蝶昆虫分类-图像分类数据集
  • 【力扣hot100题】(067)寻找两个有序数组的中位数
  • 壹起航:15 年深耕,助力中国工厂出海远航产品出海
  • Valgrind——内存调试和性能分析工具
  • 蓝桥杯真题——前缀总分、遗迹
  • el-table,新增、复制数据后,之前的勾选状态丢失
  • 网站优化连云港哪家强?/小米口碑营销案例
  • 枞阳县建设局网站/百度推广在哪里
  • php 网站后台管理系统/专业的网络推广
  • 网站开发自学网/色盲测试图数字
  • 湛江电子商务网站建设/手机百度云电脑版入口
  • 阿里云绑定wordpress/临沂seo建站