ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C语言梦开始的地方18:联合和枚举

C语言梦开始的地方18:联合和枚举 文章目录C语言梦开始的地方18联合和枚举联合体联合体类型的声明联合体的特点联合体大小的计算枚举类型枚举类型的声明枚举类型的优点枚举类型的使用C语言梦开始的地方18联合和枚举上一章我们了解了自定义类型的结构体本章我们继续了解自定义类型的联合体和枚举类型联合体联合体类型的声明像结构体一样联合体也是由一个或者多个成员构成这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫共用体。给联合体其中一个成员赋值其他成员的值也跟着变化。#includestdio.hunionUn{chara;intb;};intmain(){unionUn un{0};printf(%d\n,sizeof(un));return0;}答案是4。联合体的特点为什么是4呢我们通过下面代码来看一下#includestdio.hunionUn{chara;intb;};intmain(){unionUn un{0};un.b0x11223344;printf(%x\n,un.b);un.a0x55;printf(%x\n,un.b);return0;}运行后发现原本 un.b 的低字节 0x44 被 un.a 的赋值 0x55 覆盖了。那么是不是说明联合体的成员变量它们都看向同一块内存空间所以答案才是4字节。但是该代码还是无法解释为什么该联合体大小为4因为即使两个变量是独立的对其中任意一个赋值当然也会改变它自身的值。我们再来看下面这个代码#includestdio.hunionUn{chara;intb;};intmain(){unionUn un{0};printf(%p\n,(un.a));printf(%p\n,(un.b));printf(%p\n,un);return0;}程序运行后打印的地址是一模一样的那么我们结合这两个代码就可以给出结论联合体的成员变量是共用同一块内存空间的这样一个联合变量的大小至少是最大成员的大小因为联合体至少得有能力保存最大的那个成员。通过上述代码和结论我们能够得到un的内存布局图联合体大小的计算联合体大小的计算联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候就要对齐到最大对齐数的整数倍。#includestdio.hunionUn1{charc[5];inti;};unionUn2{shorts[7];inti;};intmain(){unionUn1 un1{0};unionUn2 un2{0};printf(Un1的大小: %d\n,sizeof(un1));printf(Un2的大小: %d\n,sizeof(un2));return0;}Un1大小最大成员 c[5] 占5字节但是char类型不是最大对齐数所以这里使用int类型即4对齐后大小为8Un2大小最大成员 s[7] 占14字节同Un1int 对齐数为4对齐后大小为16那么联合体有什么用呢或者联合体的场景是什么样的呢我们可以使用联合体来节省空间的例如我们要举办一个抽奖活动礼品有书籍杯子衬衫等礼品那么每一种商品都有库存量、价格、商品类型和商品类型相关的其他信息。书籍书名、作者、页数杯子设计衬衫设计、可选颜色、可选尺寸使用我们上一章节的结构体就会得出下面这个代码structgift_list{// 公共属性intstock_number;// 库存量doubleprice;// 定价intitem_type;// 商品类型// 特殊属性chartitle[20];// 书名charauthor[20];// 作者intnum_pages;// 页数chardesign[30];// 设计intcolors;// 颜色intsizes;// 尺寸};上述的结构其实设计的很简单用起来也方便但是结构的设计中包含了所有礼品的各种属性这样使得结构体的大小就会偏大比较浪费内存。因为对于礼品兑换单中的商品来说只有部分属性信息是常用的。比如商品是图书就不需要design、colors、sizes。所以我们就可以把公共属性单独写出来剩余属于各种商品本身的属性使用联合体起来这样就可以介绍所需的内存空间一定程度上节省了内存。structgift_list{// 公共属性intstock_number;// 库存量doubleprice;// 定价intitem_type;// 商品类型// 特殊属性union{struct{chartitle[20];// 书名charauthor[20];// 作者intnum_pages;// 页数}book;struct{chardesign[30];// 设计}mug;struct{chardesign[30];// 设计intcolors;// 颜色intsizes;// 尺寸}shirt;}item;};那么联合体除了节省空间还能做什么呢我们之前了解过大小端字节序是什么和如何来判断大小端字节序的方法当时给定的代码是intcheck_sys(){inti1;return(*(char*)i);}intmain(){intretcheck_sys();if(ret1){printf(小端\n);}else{printf(大端\n);}return0;}那么我们可以利用联合体的特性联合体的成员变量共用同一块内存空间来实现。intcheck_sys(){union{inti;charc;}un;un.i1;returnun.c;// 返回1是小端返回0是大端}intmain(){if(check_sys()1){printf(小端字节序\n);}else{printf(大端字节序\n);}return0;}枚举类型枚举类型的声明枚举顾名思义就是一一列举。把可能的取值一一列举。比如我们现实生活中一周的星期一到星期日是有限的7天可以一一列举性别有男、女、保密也可以一一列举月份有12个月也可以一一列举三原色也是可以意义列举这些数据的表示就可以使用枚举了。enumDay{Mon,Tues,Wed,Thur,Fri,Sat,Sun};enumSex{MALE,FEMALE,SECRET};enumColor{RED,GREEN,BLUE};以上定义的enum Dayenum Sexenum Color都是枚举类型。{}中的内容是枚举类型的可能取值也叫枚举常量。枚举类型默认从0开始如果给定了值就是按给定的开始比如enum Day中的Mon 1那么就是从1开始直到7即Sun。如果给2那么Sun就为8。枚举类型的优点感觉枚举类型也没什么用啊我们可以使用 #define 定义常量为什么非要使用枚举因为枚举类型增加代码的可读性和可维护性和#define定义的标识符比较枚举有类型检查更加严谨。便于调试预处理阶段会删除#define 定义的符号使用方便一次可以定义多个常量枚举常量是遵循作用域规则的枚举声明在函数内只能在函数内使用我们先要了解一个知识点预处理阶段#define会进行文本替换比如我们使用#define定义Day那么在预处理阶段凡是使用到#define的Day都会被替换成1或者7这种数据这样我们在调试如果是刚开始写项目时还能看懂但是稍微编写代码时间一长时就有可能忘记这里为什么会是1到7这种数据而使用enum就没这种担心。其次就是使用#define有可能漏掉一些数据比如说还是星期在使用#define定义时漏写了Thur在switch的case中#define会顺利通过因为int类型有42亿个可能值但是这里使用enum就发生警告并说明枚举值Thur未在switch中处理。枚举类型的使用#includestdio.henumSize{SMALL3,MEDIUM,LARGE};intmain(){enumSizesMEDIUM;if(s0){printf(Positive\n);}else{printf(Negative\n);}return0;}这个代码会不会报错为什么我们要回答这个问题就要先了解一个知识点C语言C11标准规定如果一个 int 可以表示原始类型的所有值那么该类型的值会被提升为 int。那么我们就能认为该代码不会报错因为SMALL3, MEDIUM4, LARGE5的数据范围都没超过int所以这里的判断和赋值会被提升成int类型的赋值和判断那么什么情况它才会报错呢你给定一个超出int范围的值就可以比如enum Size { SMALL INT_MAX, MEDIUM, LARGE }; // 因为 MEDIUM INT_MAX 1超出 int 范围编译器直接报错。那么C语言可以全面兼容C语言的C可以吗分情况讨论如果是enum可以如果是 enum class 不可以并需使用强制类型转换才行。那么为什么C语言可以呢因为CPU在底层根本不知道你这个到底是什么在CPU面前它就是一个数据这就导致CPU根本就没有枚举类型这个概念再加上时代的原因以前的x86 CPU支持8位和16位的算术指令但现代CPU的“主场”是32位或64位。因为8位和16位在进行加法时会通过一些手段来保证运算的正确性这样就导致运行时速率会慢不少。但C语言的建立之初就是为了极致的效率。既然转成int跑得最快那标准就强制规定不管你在哪运算前一律给我转成int再进寄存器。完
返回列表