C 语言学习笔记(结构体1)

内容提要

  • 构造类型
    • 结构体
    • 共用体/联合体

构造类型

数据类型

1.基本类型/基础类型

  • 整型

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

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

    • 长整型:long -- 32位系统4字节/64位系统8字节

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

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

  • 浮点型

    • 单精度型:float -- 4字节
    • 双精度型:double -- 8字节
    • 长双精度型:long double -- C99新增
  • 字符型:char -- 1字节

2.指针类型

  • 数据类型*:int* char* float*等 --32位系统4字节/64位系统8字节
  • void*:通用类型指针(万能指针)-- 32位系统4字节/64位系统8字节

3.空值类型

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

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

  • 结构体类型:struct
  • 共用体/联合体类型:union
  • 枚举类型:enum

结构体

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

  • 语法

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

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

  • 格式说明

    • 结构体名:合法的表示符,建议首字母大写(所谓的结构体名,就是自定义类型的类型名称)
    • 数据类型n:C语言支持的所有类型(包括函数,函数在这里用函数指针表示)
    • 成员名称n:合法的表示,就是变量的命名标准
    • 数据类型n成员名称n:类似于定义变量,定义了结构体中的成员
  • 注意

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

    • 方式1:常规定义(命名结构体,只定义数据类型)
    c 复制代码
    struct Student
    {
        int num;        // 学号
        char name[20];  // 姓名
        char sex;       // 姓名
        int age;        // 年龄
        char address[100]; // 籍贯
        void(*info)(void); // 信息输出(函数指针)
    }
    • 方式2:匿名结构体(产用于其他结构体的成员使用)
    c 复制代码
    struct Dog
    {
        char *name;    // 姓名
        int age;       // 年龄
        // 匿名结构体
        struct
        {
            // 定义结构体是不能省略成员
            int year;   // 年
            int month;  // 月
            int day;    // 日
        }birthday;      // 生日,匿名结构需要提供成员名称,否则无法访问
    }

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

    总结:

    • 结构体可以定义在局部位置(函数作用域、块作用域),也可以定义在全局位置(推荐,可以复用)
    • 全局位置的结构体名和局部位置的结构体名可以相同,遵循就近原则(和变量的定义同理)
  • 结构体类型的使用

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

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

说明 :结构体变量也被称之为结构体实施。

  • 第1种

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

    ② 然后定义结构体变量(定义变量)

    示例:

    c 复制代码
    // 定义结构体(定义数据类型)
    struct A
    {
        int a;
        char b;
    };
    
    // 定义结构体实例(定义变量)
    struct A x; // A就是数据类型,x就是变量名
    struct A y; // A就是数据类型,y就是变量名
  • 第2种

    ① 在定义结构体的同时,定义结构体变量(同时定义数据类型和变量)

    语法:

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

    示例:

    c 复制代码
    struct A
    {
        int a;
        char b;
    } x,y; // A就是数据类型,x,y就是变量名

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

  • 第3种

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

    c 复制代码
    struct
    {
        int a;
        char b;
    } x,y;

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

匿名机构体
  • 优点:少写一个结构体名称
  • 缺点:只能使用一次,定义结构体类型的同时必须定义变量。
  • 应用场景:
    • 当结构体的类型只需要使用一次,并且定义类型的同时定义了变量。
    • 作为其他结构体的成员使用。
定义结构体同时变量初始化

说明:定义结构体的同时,定义结构体变量并初始化

c 复制代码
struct Cat
{
    int age;
    char color[20];
}cat;
  • 结构体成员部分初始化,大括号不能省略
  • 结构体成员,没有默认值,都是随机值,和局部作用域的变量一致。

案例:

c 复制代码
/**
* 先定义结构体,再定义结构体变量(实例)
*/
void fun1()
{
    // 定义结构体
    struct A
    {
        int a;
        char b;
    };
    
    // 定义结构体变量
    struct A x;
    struct A y;
    struct A x1, y1;
}

/**
* 定义结构体的同时定义结构变量
*/
void fun2()
{
    struct A
    {
        int a;
        char b;
    } x,y;
    
    struct A Z;
    struct A x1,y1;
}

/**
* 定义结构体的同时定义结构体变量
*/
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;
}
结构体变量的使用【变量存取】
结构体变量访问成员
  • 语法

    c 复制代码
    结构体变量名(示例名), 成员名;

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

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

  • 结构体变量未初始化,结构体成员的值随机的(和局部作用域的变量和数组同理)

结构体变量定义时初始化
  • 建议用大括号{}标明数据的范围

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

  • 案例

    c 复制代码
    /* 定义全局的结构体,方便复用 */
    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;
        
        // 结构体变量成员访问
        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;
    }

    扩展版:给函数指针指向的函数,绑定了参数

    c 复制代码
    /* 定义全局的结构体,方便复用 */
    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;
        
        // 结构体变量成员访问
        printf("%s,%d,%c\n", dog.name, dog.age, dog.sex);
        // 访问成员方法(函数)
        dog.eat(dog.name);
    }
    
    /**
    * 方式2:定义的同时初始化 ---> 结构体变量定义时初始化
    */
    void fun2()
    {
        // 定义结构体变量的同时,给变量成员初始化
        struct Dog dog = {"旺财", 5,'M', eat};
        // 修改成员的值
        dog.name = "金宝";
        
        // 访问结构体变量成员
        printf("%s.%d.%c\n", dog.name, dog.age, dog.sex);
        // 访问成员方法(函数)
        dog.eat(dog.name);
    }
    
    int main(int argc,char *argv[])
    {
        fun1();
        fun2();
    
        return 0;
    }
结构体数组的定义【数组定义】
什么时候需要结构体数组

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

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

四种形式定义结构体数组
  • 第1种:结构体 → 结构体变量 → 结构体数组,举例:
c 复制代码
// 定义一个学生结构体(定义数据类型)
struct Student
{
    char *name;      // 姓名
    int age;         // 年龄
    float scores[3]; // 三门课程的成绩
};

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

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

}stus[3];

// 赋值
suts[0].name = "张三";
suts[0].age = 21;
suts[0].scores[0] = 88;
suts[0].scores[1] = 89;
suts[0].scores[2] = 78;

suts[1].name = "李四";
suts[1].age = 22;
suts[1].scores[0] = 66;
suts[1].scores[1] = 78;
suts[1].scores[2] = 98;
结构体数组的访问【数组访问】

语法

c 复制代码
结构体指针 -> 成员名

举例

c 复制代码
// 方式1:普通的指针访问
(*p).成员名
// 方式2:结构体指针访问(结构体指针访问符号 ->),等价与上面写法
p -> 成员名

例子

c 复制代码
/* 定义全局的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);
}

int main()
{
    // 定义结构体实例并初始化
    struct Student zhangsan = {1, "张三", 21,{78,88,98}, info};
    struct Student lisi     = {2, "李四", 22,{78,88,98}, info}; 
        
    // 定义结构体数组并初始化
    struct Student stus[] = {zhangsan,lisi};
    
    // 计算数组的大小
    int len = sizeof(stus) / sizeof(stus[0]);
    
    // 指针法遍历数组
    struct Student *p = stus;
    
    // 表格-表头
    printf("序号\t姓名\t年龄\t语文\t数学\t英语\t\n")
    for (; p < stus + len; p++)
    {
        /*  普通指针的访问,不推荐
        printf("%d\t%s\t%d\t%.2f\t%.2f\t%.2f\t\n", (*p).id, (*p).name, (*p).age, (*p).scores[0], (*p).scores[1], (*p).scores[2]);
        (*p).info(*p.name, (*p).age);      */
        
        // 结构体指针访问
        printf("%d\t%s\t%d\t%.2f\t%.2f\t%.2f\t\n", p -> id, p -> name, p -> age, p -> scores[0], p -> scores[1], p -> scores[2]);
        p -> info(p -> name, p -> age); 
    }
    
    printf("\n");
    
    return 0;
}

注意:当 -> []共存的时候,他们的优先级关系:[] > ->

结构体类型

结构体数组
案例:
  • 需求:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人名字,要求最后输出各人得票的结果。
  • 代码:
c 复制代码
#include <stdio.h>
#include <string.h>

/**
* 定义一个候选人结构体
*/
struct Person
{
    char name[20];    // 名字
    int count;        // 票数
};

/**
* 定义候选人数组,并初始化
*/
struct Person persons[] = {
    {"张三", 0},
    {"李四", 0},
    {"王五",0}
};

int main()
{
    // 定义循环变量
    int i, j;
    
    // 创建一个数组,用来接收控制台录入的候选人名字
    char leader_name[20];
    
    // 使用一个for循环,模拟10个人投票
    for(i = 0; i < 10; i++)
    {
        printf("请输入您要投票的候选人姓名:\n");
        scanf("%s, leader_name");
        
        // 从候选人列表中匹配被投票的人 +1票
        for(j = 0; j < sizeof(persons) / sizeof(persons[0]); j++)
        {
            // 判断两个字符串 strcmp
            if(strcmp(leader_name, persons[j].name) == 0)
            {
                persons[j].count++;
            }
        }
    }
    
    printf("\n投票结果:\n");
    
    // 遍历数组:指针法
    struct Person *p = persons;
    for(;p < persons + 3; p++)
    {
        // printf(" %s:%d\n",(*p).name,(*p).count);
        printf(" %s:%d\n",p -> name,p -> count);
    }
    pritnf("\n");
    
    // 遍历数组:指针法
    for(i = 0; i < 3; i++)
    {
        printf(" %s:%d\n",(persons + i) -> name, (persons + i) -> count);
    }
    pritnf("\n");
    
    // 遍历数组:下标法
    for(i = 0; i < 3; i++)
    {
        printf(" %s:%d\n",persons[i].name, persons[i].count);
    }
    pritnf("\n");
    
    return 0;
}
结构体指针
  • 定义 :结构体类型的指针变量指向结构体变量或者数组的起始地址。
  • 语法
c 复制代码
struct 结构体名 *指针变量列表;
  • 举例
c 复制代码
struct Dog
{
    char namr[20];
    int age;
};

struct Dog dog = {"苟富贵", 5};

// 基于结构体的结构体指针
struct Dog *p = &dog;
结构体成员的访问
结构体成员访问
  • 结构体数组访问结构体成员

    • 语法:

      c 复制代码
      结构体数组名 -> 成员名;
      (*结构体数组名).成员名; // 等价于上面写法
    • 举例:

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

    • .:左侧是结构体变量,也可以叫做结构体对象访问成员符,右侧是结构体成
    • ->:左侧是结构体指针,也可以叫做结构体指针访问成员符,右侧是结构体成员
    • 举例:
    c 复制代码
    struct Persin *p = persons; // p机会结构体指针
    for(;p < persons + len; p++)
        printf("%s:%s\n", p->name, p->count);
  • 访问结构体成员有两种类型,三种方式:

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

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

      • 第1种:指针访问成员

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

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

      c 复制代码
      struct Stu
      {
          int id;          // 编号
          char name[20];   // 名字(成员) 
          float scores[3]; // 三门成绩(成员)
      }stus[3] = 
      {
          {1, "张三", {90,89,78}},
          {2, "李四", {90,88,78}},
          {3, "王五", {77,89,78}},
      };
      
      // 取数据 --- 下标法
      printf("%s,%.2f\n", stus[1].name, stus[1].scores[1]); // 李四 88
      // 取数据 --- 指针法
      printf("%s,%.2f\n", stus -> name, stus -> scores[2]); // 张三 78
      printf("%s,%.2f\n", (stus+1) -> name, (stus+1) -> scores[1]); // 李四 88 
      printf("%s,%.2f\n", (*(stus+1)).name, (*(stus+1)).scores[1]); // 李四 88 

    小贴士:

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

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

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

  • 结构体类型的使用案例

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

    举例:

    c 复制代码
    #include <stdio.h>
    #include <string.h>
    
    /**
    * 定义一个结构体(数据类型)
    */
    struct Cat
    {
        char *namr;       // 姓名
        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, char* name)
    {
        struct Cat *p = cats;
        for(; p < cats + len; p++)
        {
            if(strcmp(name, p -> name) == 0) return p;
        }
        return NULL;
    }
    
    /**
    * 结构体指针作为形式参数
    */
    struct Cat *test4(struct Cat *cats, int len, char* name)
    {
        struct Cat *p = cats;
        for(; p < cats + len; p++)
        {
            if(strcmp(name, p -> name) == 0) return p;
        }
        return NULL;
    }
    
    int main()
    {
        // 定义结构类型
        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"}
        };
        
        // 结构体数组作为参数
        struct Cat *p1 = test3(cats,3,"汤姆");
        printf("test2:\n%s,%d,%s\n", p1 -> name, p1 -> age, p1 -> color);
        
        // 结构体指针作为参数
        struct Cat *p2 = test4(cats,3,"汤姆");
        printf("test2:\n%s,%d,%s\n", p2 -> name, p2 -> age, p2 -> color);
        
        return 0;
    }
结构体类型求大小
字节对齐
  • 字节对齐的原因:

    1. 硬件要求 某些硬件平台(如ARM、x86)要求特定类型的数据必须对齐到特定地址,否 则会引发性能下降或硬件异常。
    2. 优化性能 对齐的数据访问速度更快。例如,CPU访问对齐的int数据只需一次内存操作,而未对齐的数据可能需要多次操作。
  • 字节对齐规则:

    1. 默认对齐规则
    • 结构体的每个成员按其类型大小和编译器默认对齐数(通常是类型的自然对齐数)对齐。
    • 结构体的总大小必须是最大对齐数的整数倍。
    1. 对齐细节
    • 基本类型的对齐数char (1字节)、 short (2字节)、 int (4字节)、double(8字节)。
    • 结构体成员的对齐 :每个成员的起始地址必须是对齐数的整数倍。
    • 结构体总大小的对齐 :结构体的总大小必须是其最大对齐数的整数倍。
    1. #pragma pack(n)的影响 使用#pragma pack(n) 可以强制指定对齐数为 nn 为 1、2、4、8、16)。此时:
      • 每个成员的对齐数取 n 和其类型大小的较小值。
      • 结构体的总大小必须是n和最大对齐数中的较小值的整数倍。
  • 对齐示例

    • 默认对齐

      c 复制代码
      struct s1
      {
          char c;   // 1字节,偏移0
          int i;    // 4字节,(需对齐到4,填充3字节,偏移4-7)
          double d; // 8字节(需对其到8,偏移8~15)
      } // 16字节
      c 复制代码
      struct s2
      {
          double d; 
          char c;   
          int i; 
      } // 16字节
      c 复制代码
      struct s3
      {
          char c; 
          double d;   
          int i; 
      } // 24字节

      内存分析:

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

  • 使用#param pack(1)

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

    c 复制代码
    __attribute__((packed));      -- 结构体所占内存大小是所有成员所占内存大小之和
    --attribute__((aligned(n)));  -- 设置结构体占n个字节,如果n比默认值小,n不起作用;n必须是2的次方
  • 案例:

    c 复制代码
    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        struct cat
        {
            char sex_attribute((aligned(2)));//4 2
            int id;//4
            char name[20];//20
        }_attribute_((packed));//结构体所占内存大小是所有成员所占内存大小之和
        
        printf("%]d\n",sizeof(struct cat));//默认字节对齐(28)/使用packed后(25)
        return 0;
    }

柔性数组

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

语法:

c 复制代码
struct St
{
    ...
        char arr[0];
}

案例:

c 复制代码
#include <stdio.h>
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;
 }

共用体/联合体类型

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

  • 定义格式

    c 复制代码
    union 共用体名称
    {
        数据类型 成员名;
        数据类型 成员名;
        ...
    }
  • 共用体的定义和机构体类似。

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

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

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

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

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

    • ...

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

  • 注意:

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

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

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

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

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

    c 复制代码
    /**
    * 定义共用体
    */
    
    union S
    {
        cahr a;
        float b;
        int c;
    }; // S的大小是4字节
    
    // 共用体作为共用体成员
    union F
    {
        char a;
        union S s; // 4字节
    }; // F的大小是4字节
    
    // 定义一个结构体
    struct H
    {
        int a;
        char b;
    }; // H的大小是8字节
    
    // 结构体作为结构体成员
    struct I
    {
        int a;
        int b;
        struct H h;
    }; // I的大小是16;
    
    // 共用体作为结构体成员
    strcut J
    {
        int a;  // 4
        char b; // 1 + 3
        union S s; // 4
    }; // J的大小是12
    
    void test1()
    {
        // 定义一个共用体
        union Obj
        {
            int num;
            char sex;
            double score;
        };
        
        // 定义匿名共用体
        union
        {
            int a;
            char c;
        } c;
        
        // 定义变量
        union Obj obj;
        // 存储数据
        obj.num = 10; // 共用体空间数据:10
        obj.sex = 'A';// 共用体空间数据:'A' = 65
        
        // 运算
        obj.num += 5; // 共用体空间数据:70 覆盖数据 'F'
        
        printf("%lu,%d,%c,%.2lf\n",sizeof(obj),obj.num, obj.sex, obj.score);
    }
    
    int main()
    {
        text1();
        
        return 0;
    }

可以是结构体的成员

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

    c 复制代码
    /**
    * 定义共用体
    */
    
    union S
    {
        cahr a;
        float b;
        int c;
    }; // S的大小是4字节
    
    // 共用体作为共用体成员
    union F
    {
        char a;
        union S s; // 4字节
    }; // F的大小是4字节
    
    // 定义一个结构体
    struct H
    {
        int a;
        char b;
    }; // H的大小是8字节
    
    // 结构体作为结构体成员
    struct I
    {
        int a;
        int b;
        struct H h;
    }; // I的大小是16;
    
    // 共用体作为结构体成员
    strcut J
    {
        int a;  // 4
        char b; // 1 + 3
        union S s; // 4
    }; // J的大小是12
    
    void test1()
    {
        // 定义一个共用体
        union Obj
        {
            int num;
            char sex;
            double score;
        };
        
        // 定义匿名共用体
        union
        {
            int a;
            char c;
        } c;
        
        // 定义变量
        union Obj obj;
        // 存储数据
        obj.num = 10; // 共用体空间数据:10
        obj.sex = 'A';// 共用体空间数据:'A' = 65
        
        // 运算
        obj.num += 5; // 共用体空间数据:70 覆盖数据 'F'
        
        printf("%lu,%d,%c,%.2lf\n",sizeof(obj),obj.num, obj.sex, obj.score);
    }
    
    int main()
    {
        text1();
        
        return 0;
    }
相关推荐
码农Cloudy.42 分钟前
数据结构 --- 顺序表
c语言·数据结构
小葡萄20251 小时前
黑马程序员C++核心编程笔记--4 类和对象--封装
java·c++·笔记
与己斗其乐无穷1 小时前
数据结构(7)树-二叉树-堆
数据结构·学习
ghost1432 小时前
C#学习26天:内存优化的几种方法
开发语言·学习·性能优化·c#
浪淘沙jkp4 小时前
AI大模型学习三十、ubuntu安装comfyui,安装插件,修改返回405 bug,值得一看喔
人工智能·学习·ubuntu·comfyui·dify
魔术师ID4 小时前
微信小程序学习目录
学习·微信小程序·小程序
老神在在0018 小时前
javaEE1
java·开发语言·学习·java-ee
Always_away10 小时前
26考研|高等代数:λ-矩阵
笔记·学习·线性代数·矩阵
DevangLic10 小时前
ffmpeg baidu
人工智能·pytorch·python·学习·ai·ffmpeg
图梓灵10 小时前
Maven与Spring核心技术解析:构建管理、依赖注入与应用实践
java·笔记·spring·maven