联合体类型
联合体的声明:
union 联合体名{
成员列表
}变量列表;
注意事项:
1、联合体所有成员共⽤同⼀块内存空间,它们的地址也相同
#include <stdio.h> union Un { char c; int i; }; int main() { union Un un = {0}; printf("%p\n", &(un.i)); printf("%p\n", &(un.c)); printf("%p\n", &un); return 0; }
2、给联合体其中一个成员赋值,其他成员的值也跟着变化
#include <stdio.h> union Un { char c; int i; }; int main() { union Un un = {0}; un.i = 0x11223344; un.c = 0x55; printf("%x\n", un.i); return 0; }
un.i在存中的变化:
当我们对un.c赋值0x55的时候,un.i的值发生了改变:
联合体的大小计算
计算规则:
联合体大小的计算规则与结构体大小的计算规则(内存对齐)有相似之处
1、联合的⼤⼩⾄少是最⼤成员的⼤⼩
2、联合体最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍
#include <stdio.h> union Un1 { char c[5]; //5字节 1 8 1 int i; //4字节 4 8 4 };0 union Un2 { short c[7];//14字节 2 8 2 int i; //4字节 4 8 4 }; int main() { printf("%d\n", sizeof(union Un1)); printf("%d\n", sizeof(union Un2)); return 0; }
①Un1中最大成员大小为5,最大对齐数为4,所以联合体大小为8
②Un2中最大成员大小为14,最大对齐数为4,所以联合体大小为16
联合体的实际应用
我们要搞⼀个活动,要上线⼀些礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。 每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨
可以直接写出⼀下结构:
struct gift_list { //公共属性 int stock_number;//库存量 double price; //定价 int item_type;//商品类型 //特殊属性 char title[20];//书名 char author[20];//作者 int num_pages;//⻚数 char design[30];//设计 int colors;//颜⾊ int sizes;//尺⼨ };
上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的⼤⼩偏⼤,浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常⽤的。⽐如:商品是图书,就不需要design、colors、sizes这些属性。 所以我们应当把公共属性单独写出来,剩余的属于各种商品本⾝的特殊属性放进联合体中:
/
#include <stdio.h> struct gift_list { //通用属性 int stock_number;//库存量 double price;//定价 int item_type;//商品类型 //特殊属性 union { struct { char title[20];//书名 char author[20];//作者 int num_pages;//页数 }books; struct { char design[30];//设计 }mug; struct { char design[30];//设计 int colors;//颜色 int sizes;//尺寸 }shirt; }items; }; int main() { struct gift_list gl = {0}; gl.stock_number = 100; gl.price = 25; gl.item_type = 2; gl.items.books = {"wode","nde",30}; printf("%s,%s,%d", gl.items.books.title, gl.items.books.author, gl.items.books.num_pages); return 0; }
练习:判断大小端
#include <stdio.h> int check() { int a = 1; return (*(char*)&a); } int main() { int a = 1; if (check) printf("小端\n"); else printf("大端\n"); return 0; }
利用联合体实现大小端的判断:
#include <stdio.h> int check_sys() { union { int i; char c; }un; un.i = 1; return un.c; } int main() { if (check_sys() == 1) printf("小端\n"); else printf("大端\n"); return 0; }
当我们给un.i赋值给1后,小端机器在内存中的存储形式为01 00 00 00,然后此时联合体内的char c也要占用一个字节的空间覆盖了原来01所在的内存空间,此时返回un.c所在的内存空间发现原来的01变为现在的00,所以为小端机器。
枚举类型
枚举就是一一列举,它可以用来列举我们日常生活中常见的各种东西,比如性别里的男和女,颜色里的赤橙黄绿青蓝紫......
枚举类型的声明:
enum 类型名 { 枚举常量 (建议全部大写) };
enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜⾊ { RED, GREEN, BLUE };
其中,enum Color和enum Sex叫做枚举类型,MALE、FEMALE等叫做枚举常量它们是枚举类型的可能取值。我们打印一下它们看看:
#include <stdio.h> enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜⾊ { RED, GREEN, BLUE }; int main() { enum Sex sex = MALE; //枚举类型的使用方式就是将枚举常量赋值给枚举类型的变量; printf("%d %d %d ", MALE, FEMALE, SECRET); return 0; }
我们会发现打印结果为0 1 2,这是以为这些枚举常量都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
枚举类型的优点
1、增加代码的可读性和可维护性
2、和#define定义的标识符⽐较枚举有类型检查,更加严谨。
3、便于调试,预处理阶段会删除 #define 定义的符号
4、使⽤⽅便,⼀次可以定义多个常量
5、枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
枚举类型的实际案例:
本案例取自本人的另一篇文:《贪吃蛇---C语言版本》
//贪吃蛇游戏中反应蛇移动方向的枚举类型 enum DIRECTION { UP, //向上 DOWN, //向下 LEFT, //向左 RIGHT //向右 }; //定义反应游戏状态的枚举类型 enum GAME_STATUS { OK, //游戏正常运⾏ KILL_BY_WALL, //撞墙 KILL_BY_SELF, //自己撞到自己 END_NOMAL //正常结束(自己选择ESC结束游戏) };
//利用switc判断ps->_Dir的不同方向 switch (ps->_Dir) { case UP: //如果蛇是向上运动的,那么蛇运动的下一个结点的x轴坐标与蛇头保持一致,y轴坐标为蛇头y轴坐标减一,下面的就不一一写解释了 pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y - 1; break; case DOWN: pNext->x = ps->_pSnake->x; pNext->y = ps->_pSnake->y + 1; break; case LEFT: pNext->x = ps->_pSnake->x - 2; pNext->y = ps->_pSnake->y; break; case RIGHT: pNext->x = ps->_pSnake->x + 2; pNext->y = ps->_pSnake->y; break; }
与switch结合是目前我所见到的唯一一个对于枚举类型的使用案例,可能还会有其他的后续学习过程中会进行适当的补充......