【C语言】自定义数据类型2——联合体与枚举

目录

一、联合体(共用体)

1.定义与使用

2.核心特点:内存共享

3.匿名联合体(c11)

[4. 主要用途](#4. 主要用途)

二、枚举

1.定义与使用

2.枚举与整数

3.枚举与#define

4.匿名枚举

5.常用场景

三、补充

1.使用联合体判断大小端

2.联合体与结构体内存对比

3.联合体占用空间大小的计算

4.使用联合体节省空间举例


一、联合体(共用体)

1.定义与使用

联合体允许在同一块内存空间中存储不同类型的数据。

与结构体不同,它的所有成员共享同一段内存,给联合体其中一个成员赋值,其他成员的值也跟着变化(因此也叫共用体)。

一个联合变量的大小,至少是最大成员的大小(因为至少得有能力保存最大的那个成员)。

复制代码
#include <stdio.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;

    data.i = 10;
    printf("data.i = %d\n", data.i);   // 输出 10

    data.f = 220.5;
    printf("data.f = %.1f\n", data.f); // 输出 220.5
    // 注意:此时 data.i 的值已经被覆盖,变成无意义的内容

    // 使用字符串成员
    sprintf(data.str, "Hello");
    printf("data.str = %s\n", data.str); // 输出 Hello

    return 0;
}

2.核心特点:内存共享

复制代码
#include <stdio.h>
//联合类型的声明 
union Un
{
    char c;
    int i;
};

/******************************* 代码1 *******************************/
int main()
{
    //联合变量的定义 
    union Un un = {0};
    // 下⾯输出的结果是⼀样的吗? 
    printf("%p\n", &(un.i));
    printf("%p\n", &(un.c));
    printf("%p\n", &un);
    return 0;
}
输出结果:
001AF85C
001AF85C
001AF85C


/******************************* 代码2 *******************************/
int main()
{
    //联合变量的定义 
    union Un un = {0};
    un.i = 0x11223344;
    un.c = 0x55;
    printf("%x\n", un.i);
    return 0;
}

输出结果:11223355
  • 大小 :联合体的大小等于其最大成员 的大小(可能因对齐而稍大)。上面的例子中,假设 int 4字节、float 4字节、char[20] 20字节,那么 sizeof(union Data) 通常是 20。

  • 同一时间只有一个成员有效:写入一个成员会覆盖其他成员的值,因为它们在内存中起点相同。

  • 访问 :用点运算符 . 访问普通变量,用箭头 -> 访问指针变量。

3.匿名联合体(c11)

可以在结构体内部直接定义一个没有名字的联合体,方便访问:

复制代码
struct Variant {
    int type;
    union {     // 匿名联合体
        int i;
        float f;
    };
};

struct Variant v;
v.type = 1;
v.i = 100;      // 直接访问,不需要额外的成员名

4. 主要用途

  • 节省内存:当某个变量在不同时刻只需要存储不同类型的数据时(例如一个变量可能存整数或浮点数),用联合体比结构体省空间。

  • 数据类型转换/查看底层表示 :比如通过把 floatunsigned int 放在同一个联合体里,来读取浮点数的二进制表示。

  • 硬件寄存器映射:在嵌入式编程中,常通过联合体将一个寄存器按整体或按位域来访问。

  • 变体记录:实现类似"一个值有多种可能类型"的数据结构。

二、枚举

1.定义与使用

复制代码
enum Weekday {
    MON,    // 默认值为 0
    TUE,    // 1
    WED,    // 2
    THU,    // 3
    FRI,    // 4
    SAT,    // 5
    SUN     // 6
};

// 定义枚举变量
enum Weekday today;
today = WED;   // 相当于 today = 2;


/********************************** 其他创建方式 **********************************/
/* 1.手动赋值所有 */
enum Color {
    RED = 1,
    GREEN = 5,
    BLUE = 10
};

/* 2.手动赋值第一个,后续自动递增 */
enum Status {
    OK = 200,
    CREATED,     // 201
    ACCEPTED,    // 202
    BAD_REQUEST = 400,
    UNAUTHORIZED // 401
};

2.枚举与整数

在C语言中,枚举类型就是整数类型 ,枚举变量可以直接赋值给 int,也允许将 int 赋给枚举变量(虽然可能存在风险)。它不像C++那样有严格的类型检查。

复制代码
enum Weekday day = WED;
int num = day;       // 没问题,num = 2
day = 100;           // 语法上允许,但逻辑上可能越界

3.枚举与#define

  • 可读性if (day == SAT)if (day == 5) 清晰得多。

  • 调试友好:调试器通常能显示枚举常量的名字,而宏只是数字。

  • 作用域控制 :枚举受作用域限制,可以放在结构体或函数内部,而 #define 是全局的。

  • 类型提示:虽然C不强制,但定义了枚举类型可以给程序员和工具更明确的意图。

4.匿名枚举

如果只是需要一组整数常量,可以不写类型名:

复制代码
enum { MAX_SIZE = 100, MIN_SIZE = 10 };
int buf[MAX_SIZE];

5.常用场景

  • 状态机(IDLE, RUNNING, STOPPED...)

  • 选项标志(SMALL, MEDIUM, LARGE)

  • 错误码(SUCCESS, ERROR_TIMEOUT, ERROR_INVALID...)

  • switch 语句的 case 标签,比直接用魔术数字清晰得多。

三、补充

1.使用联合体判断大小端

复制代码
int check_sys()
{
    union
    {
        int i;
        char c;
    }un;

    un.i = 1;
    return un.c;//返回1是⼩端,返回0是⼤端 
}

2.联合体与结构体内存对比

复制代码
struct S
{
    char c;
    int i;
};
struct S s = {0};


union Un
{
    char c;
    int i;
};
union Un un = {0};

3.联合体占用空间大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

4.使用联合体节省空间举例

一个经典的例子是变体记录(Tagged Union)------用联合体来存储"根据类型不同而属性不同"的数据。比如在一个学生管理系统中,小学生、中学生、大学生有不同的专属信息,如果不使用联合体,你可能会把所有可能的属性都塞进一个结构体里,造成巨大浪费。

复制代码
#include <stdio.h>

// 学生类型
#define TYPE_PRIMARY   0
#define TYPE_MIDDLE    1
#define TYPE_UNIVERSITY 2

struct Student {
    char name[32];
    int type;
    
    // 所有类型可能用到的字段全都定义上
    char primary_parent_phone[20];  // 小学生:家长电话
    int  middle_class_grade;        // 中学生:班级排名
    char university_major[30];      // 大学生:专业
    int  university_year;           // 大学生:年级
};

/*
在这个结构体里,一个小学生根本不用的 university_major 也要占30个字节,
一个大学生不用的 middle_class_grade 也占4个字节。
总大小大约是 32 + 4 + 20 + 4 + 30 + 4 = 94 字节(再加上对齐填充,可能接近100字节)。
*/

#include <stdio.h>

#define TYPE_PRIMARY   0
#define TYPE_MIDDLE    1
#define TYPE_UNIVERSITY 2

struct Student {
    char name[32];
    int type;
    
    union {
        // 小学生专属数据
        struct {
            char parent_phone[20];
        } primary;
        
        // 中学生专属数据
        struct {
            int class_grade;
        } middle;
        
        // 大学生专属数据
        struct {
            char major[30];
            int  year;
        } university;
    } info;   // 联合体变量
};
/*
此时,info 联合体的大小等于最大成员的大小(大学生那块,30+4=34 字节,加上可能的对齐),
整个结构体比之前少了至少24字节。
对于一个记录,节省可能不多,但如果有10万个学生的数组,就能节省约2.4 MB以上的内存。
*/


/************************************ 使用方法 ************************************/
void print_student(const struct Student *s) {
    printf("姓名: %s\n", s->name);
    switch (s->type) {
        case TYPE_PRIMARY:
            printf("家长电话: %s\n", s->info.primary.parent_phone);
            break;
        case TYPE_MIDDLE:
            printf("班级排名: %d\n", s->info.middle.class_grade);
            break;
        case TYPE_UNIVERSITY:
            printf("专业: %s, 年级: %d\n",
                   s->info.university.major,
                   s->info.university.year);
            break;
    }
}

int main() {
    struct Student s = {
        .name = "张三",
        .type = TYPE_UNIVERSITY,
        .info.university = {"计算机科学", 3}
    };
    print_student(&s);
    return 0;
}
相关推荐
之歆3 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
jolimark3 小时前
C语言自学攻略:小白入门三步走
c语言·编程入门·学习路线·实践项目·自学攻略
cen__y4 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git
AI人工智能+电脑小能手4 小时前
【大白话说Java面试题 第65题】【JVM篇】第25题:谈谈对 OOM 的认识
java·开发语言·jvm
社交怪人4 小时前
【算平均分】信息学奥赛一本通C语言解法(题号2071)
c语言·开发语言
x_yeyue5 小时前
三角形数
笔记·算法·数论·组合数学
郭涤生5 小时前
不同主机之间网络通信-以太网连接复习
开发语言·rk3588
山居秋暝LS5 小时前
【无标题】RTX00安装paddle OCR,win11不能装最新的,也不能用GPU
开发语言·r语言
卢锡荣5 小时前
单芯通吃,盲插标杆 —— 乐得瑞 LDR6020,Type‑C 全场景互联 “智慧芯”
c语言·开发语言·计算机外设