联合体和枚举——嵌入式学习笔记
目录
前言
一、联合体(共用体)
1、基本概念
2、初始化和引用
(1)初始化
(2)引用
二、枚举
前言
在C语言的编程世界中,我们早已熟悉了结构体
struct
这种能将不同数据类型捆绑在一起的“打包”神器。但你是否想过,如果有一块内存,可以灵活地存放不同类型的数据,但在同一时刻只使用其中一种,该如何实现?又是否曾为那些 magic number(魔法数字)和意义不明的整数常量而头疼?这就是联合体(
union
)和枚举(enum
)大显身手的地方。它们一个是高效利用内存的“变体”专家,一个是提升代码可读性与安全性的“语义化”大师。本篇博客将带你深入浅出,探索这两个强大工具的奥秘,让你的C代码更加专业、优雅和高效。
一、联合体(共用体)
1、基本概念
说明:
联合体的外在形式跟结构体非常相似,但它们有一个本质的区别:结构体中的各个成员各自独立,而联合体的各个成员却共用一块内存,因此联合体也称为共用体
图解:
特点:
整个联合体变量的尺寸,取决于联合体尺寸最大的成员
给联合体的某个成员赋值,会覆盖其他成员,使它们失效
联合体各成员之间形成一个 "互斥"的逻辑的,在某一个时刻只有一个成员有效
语法:
联合体标签:用来区分各个不同的联合体
成员: 是包含在联合体内部的数据,可以是任意的数据类型
基本结构:
union 联合体标签 {成员1;成员2;... };
2、初始化和引用
(1)初始化
说明:
联合体的操作跟结构体形式上别无二致,但由于联合体特殊的存储特性,不管怎么初始化和赋值,最终有且仅有一个成员是有效的
示例代码:
#include <stdio.h>union node1 {char ch; // A、B、C、Dint num; // 60、70、80、90、100double f; // 99.9、59.9char buf[128]; // 优秀、良好、中等、及格、差 };union node2 {char ch; // A、B、C、Dint num; // 60、70、80、90、100 };// 主函数 int main(int argc, char const *argv[]) {// (1)、联合体的初始化// 1、普通初始化union node1 n1 = {'A', 90, 99.9, "优秀"};printf("联合体成员的值 == %d\n", n1.ch);printf("n1的大小为 == %lu\n", sizeof(n1)); // - 整个联合体变量的尺寸,取决于联合体尺寸最大的成员/*报警告:warning: excess elements in union initializer// 翻译:警告:联合体初始化器中的元素过多原因:只有第一个成员有效,其它成员赋值无效,也无需赋值- 给联合体的某个成员赋值,会覆盖其他成员,使它们失效- 联合体各成员之间形成一个 "互斥"的逻辑的,在某一个时刻只有一个成员有效*/// 2、指定成员初始化(只有最后一个赋值有效,其它赋值无效,会被覆盖)union node2 n2 ={.num = 80,.ch = 'D'};printf("联合体的成员的值 == %c\n", n2.ch);printf("联合体的成员的值 == %d\n", n2.num);printf("n2的大小为 == %lu\n", sizeof(n2));return 0; }
(2)引用
说明:
联合体一般很少单独使用,而经常以结构体的成员形式存在,用来表达某种互斥的属性
示例代码:
#include <stdio.h> #include <string.h> #include <stdlib.h>union node1 {char ch; // A、B、C、Dint num; // 60、70、80、90、100double f; // 99.9、59.9char buf[128]; // 优秀、良好、中等、及格、差 };union node2 {char ch; // A、B、C、Dint num; // 60、70、80、90、100 };typedef struct student {char name[128]; char phone[128];int id;int gender;char class[128];float height;float weight;int age;union node1 score; // 里面有四种互斥的属性}stu_t, *stu_p; // 主函数 int main(int argc, char const *argv[]) {// (1)、联合体的初始化// 1、普通初始化union node1 n1 = {'A', 90, 99.9, "优秀"};printf("联合体成员的值 == %d\n", n1.ch);printf("n1的大小为 == %lu\n", sizeof(n1)); // - 整个联合体变量的尺寸,取决于联合体尺寸最大的成员/*报警告:warning: excess elements in union initializer// 翻译:警告:联合体初始化器中的元素过多原因:只有第一个成员有效,其它成员赋值无效,也无需赋值- 给联合体的某个成员赋值,会覆盖其他成员,使它们失效- 联合体各成员之间形成一个 "互斥"的逻辑的,在某一个时刻只有一个成员有效*/// 2、指定成员初始化(只有最后一个赋值有效,其它赋值无效,会被覆盖)union node2 n2 ={.num = 80,.ch = 'D'};printf("联合体的成员的值 == %c\n", n2.ch);printf("联合体的成员的值 == %d\n", n2.num);printf("n2的大小为 == %lu\n", sizeof(n2));// (2)、联合体的引用// 1、联合体的直接引用(最后的赋值,会将整个联合体全部覆盖)n1.ch = 'C';n1.num = 120;n1.f = 59.9;strcpy(n1.buf , "优秀+");printf("n1.buf == %s\n", n1.buf);// 2、联合体的间接引用(最后的赋值,会将整个联合体全部覆盖)union node1 *p1 = &n1; // 指针指向的内存的是栈区union node1 *p2 = malloc(sizeof(union node1)); // 指针指向的内存的是堆区(无初始化)bzero(p2, sizeof(union node1));// 栈区指针strcpy(p1->buf, "良好");p1->ch = 'E';p1->num = 220;p1->f = 6.28;printf("p1->f == %f\n", p1->f);// 堆区指针strcpy(p2->buf, "中等");p2->num = 330;p2->f = 12.56;p2->ch = 'F';printf("p2->f == %c\n", p2->ch);// (3)、联合体的实际的引用例子struct student stu1 = {0};stu1.score.num = 100; /*此处可以选择一下四种评价标准,但同一时间内只能够选择其中一种ch; // A、B、C、Dnum; // 60、70、80、90、100f; // 99.9、59.9; // 优秀、良好、中等、及格、差*/return 0; }
二、枚举
说明:
枚举类型的本质是提供一种范围受限的类型,比如用0-6表示7种颜色,用0-3表示4种状态,但枚举在C语言种并未实现其本来应用的效果,直到C++才拥有原本该有的属性
是使用有意义的单词,来代替无意义的数字,提高程序的可读性...
语法:
enum是关键字
spectrum是枚举常量列表标签,省略的情况下无法定义枚举变量
格式:
enum 枚举常量列表标签 {常量1,常量2,... }
例子:
// 枚举类型 enum color{red, orange, yellow=8, green, blue, cyan, purple}; enum {reset, running, sleep, stop};// 枚举变量 enum color c = yellow; // 等价于c = 2
要点:
枚举常量实质上就是整型,首个 枚举常量默认为0
枚举常量在定义时可以赋值,若不赋值,则取值前面的枚举常量的值加1
C语言种,枚举等价于整型,支持整型数据的一切操作
示例代码:
#include <stdio.h>// stm32的枚举的使用(相比define的好处是可以归类,并且可以迅速且大规模的铺开) typedef enum IRQn {/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt *//****** STM32 specific Interrupt Numbers **********************************************************************/// ... }IRQn_Type;// 表示颜色 enum color // 有标签,所以能定义变量 {red, // 没有赋值默认为0(后面的值依次+1)orange, // 1yellow, // 2green=8, // 如果赋值了,那么从此数据开始,后面依次+1(前面数据依然遵循前面的规则)blue, // 9cyan, // 10 purple // 11 }; enum // 没有标签,所以不能定义变量 {reset, // 重启running, // 运行sleep, // 休眠stop // 停止,关机 };// 主函数 int main(int argc, char const *argv[]) {// (1)、枚举类型默认初始化和引用enum color c = 1;c = c + 1;printf("c == %d\n", c); // 枚举等价于整型,支持整型数据的一切操作1// (2)、在实际开发种使用(是使用有意义的单词,来代替无意义的数字,提高程序的可读性...)// 枚举等价于整型,支持整型数据的一切操作2// 1、颜色选择enum color c_num = 0;printf("请选择颜色:\n");printf("0、红色\n");printf("1、橙色\n");printf("2、黄色\n");scanf("%d", (int*)&c_num); while(getchar()!='\n');switch (c_num){case red:printf("我是红红!\n");break;case orange:printf("我是橙橙!\n");break;case yellow:printf("我是黄黄!\n");break;}// 2、机器状态选择同上return 0; }