C语言union总结
最近在工作中看到前辈写了串代码,使用union,加深了对C语言的认识,感叹C语言的博大精深!
需要实现的功能:
从模块有5个按键,每个按键有5种状态。主模块与从模块串口相连。当从模块按下按键时发送按键数据到串口,主模块通过串口接收的数据判断哪个按键触发及按键状态。分别为这5个按键做一个接口,方便别人直接调取来读取按键状态。
以下为设置的按键及状态的标志代码(自己稍加修改),代码1:
#include "stdio.h"
typedef unsigned char uint8_t;
typedef enum //规定STATE1=0x00 STATE2=0x01 STATE3=0x02 STATE4=0x03 STATE5=0x07
{
STATE1 = 0,
STATE2,
STATE3,
STATE4,
reserive1, //(保留三位)
reserive2,
reserive3,
STATE5
}Key_State_Type;
typedef union //KEY1-5表示5个按键,每个按键5中状态STATE1-5。
{
uint8_t _data[2]; //按键5个状态为0x01-0x07,即3个位,5个按键就是5*3=15位,因此2个u8变量即可表示5个按键
struct
{
uint8_t KEY1 :3;
uint8_t KEY2 :3;
uint8_t KEY3 :3;
uint8_t KEY4 :3;
uint8_t KEY5 :3;
uint8_t reserive :1; //(保留一位)
}Key_Type;
}Key_Value_Type;
int main(){
Key_Value_Type key;
key.Key_Type.KEY1 = STATE5;
printf("KEY1_STATE = %d\n",key.Key_Type.KEY1);
key.Key_Type.KEY2 = STATE4;
printf("KEY2_STATE = %d\n",key.Key_Type.KEY2);
printf("KEY1_STATE = %d\n",key.Key_Type.KEY1);
}
运行结果:
KEY1_STATE = 7
KEY2_STATE = 3
KEY1_STATE = 7
查阅C语言中union的资料了解到:
1. 联合体union的基本特性(和struct的相同与不同)
联合体(union)的声明和结构与结构体类似,但是本质不同。
联合的所有成员引用的是内存中的相同位置。当你想在不同时刻把不同的东西存储于同一位置时,就可以使用联合。
结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。
而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使用更为精细灵活,也节省了内存空间。
用一个例子来说明,代码2:
#include "stdio.h"
typedef unsigned char uint8_t;
typedef union
{
uint8_t union1;
uint8_t union2;
uint8_t union3;
}Union_Type;
typedef struct
{
uint8_t struct1;
uint8_t struct2;
uint8_t struct3;
}Struct_Type;
main(){
Union_Type union_type;
Struct_Type struct_type;
union_type.union1 = 5;
printf("union1 = %d\n",union_type.union1);
printf("union2 = %d\n",union_type.union2);
printf("union3 = %d\n",union_type.union3);
printf("************\n");
union_type.union2 = 8;
printf("union1 = %d\n",union_type.union1);
printf("union2 = %d\n",union_type.union2);
printf("union3 = %d\n",union_type.union3);
printf("************\n");
union_type.union3 = 1;
printf("union1 = %d\n",union_type.union1);
printf("union2 = %d\n",union_type.union2);
printf("union3 = %d\n",union_type.union3);
printf("************------------\n");
struct_type.struct1 = 5;
printf("struct1 = %d\n",struct_type.struct1);
printf("struct2 = %d\n",struct_type.struct2);
printf("struct3 = %d\n",struct_type.struct3);
printf("************\n");
struct_type.struct2 = 8;
printf("struct1 = %d\n",struct_type.struct1);
printf("struct2 = %d\n",struct_type.struct2);
printf("struct3 = %d\n",struct_type.struct3);
printf("************\n");
struct_type.struct3 = 1;
printf("struct1 = %d\n",struct_type.struct1);
printf("struct2 = %d\n",struct_type.struct2);
printf("struct3 = %d\n",struct_type.struct3);
printf("************\n");
}
运行结果:
union1 = 5
union2 = 5
union3 = 5
************
union1 = 8
union2 = 8
union3 = 8
************
union1 = 1
union2 = 1
union3 = 1
************------------
struct1 = 5
struct2 = 0
struct3 = 0
************
struct1 = 5
struct2 = 8
struct3 = 0
************
struct1 = 5
struct2 = 8
struct3 = 1
************
由此可以看出union中共用一个内存的概念了,通过代码1发现union中可以使用struct,这也是一个巧妙的用法。
2.多种访问内存途径共存(双刃剑)
举例,代码3:
#include<stdio.h>
typedef union
{
long int a;
int b;
}Union_Typedef;
main(){
Union_Typedef union_Typedef;
union_Typedef.a = 5;
printf("union_Typedef.b is %d\n", union_Typedef.b);
union_Typedef.b = 6;
printf("now union_Typedef.a is %ld! the address is %p\n",union_Typedef.a,&union_Typedef.a);
printf("now union_Typedef.b is %d! the address is %p\n",union_Typedef.b,&union_Typedef.b);
}
运行结果:
union_Typedef.b is 5
now union_Typedef.a is 6! the address is 000000000061FE1C
now union_Typedef.b is 6! the address is 000000000061FE1C
所以说,union完全就是共用一个内存首地址,并且各种变量名都可以同时使用,操作也是共同生效。如此多的access内存手段,确实好用,不过这些“手段”之间却没法互相屏蔽——就好像数组+下标和指针+偏移一样。
上例中改了union_Typedef.a的值,结果union_Typedef.b也能读取,那么也许我还以为union_Typedef.b是我想要的值呢,因为上边提到了union的内存首地址肯定是相同的,那么还有一种情况和上边类似:
一个int数组变量a,一个long int(32位机中,long int占4字节,与int相同)变量b,即使没给int变量b赋值,因为数据类型相同,使用int变量b也完全会拿出int数组a中的a[0]来,这时可能误以为使用的是变量b。
3.联合体union和大小端(big-endian、little-endian):
下边示范了一种用途,代表四个含义的四个变量,但是可以用一个int来操作,直接int赋值,无论内存访问(指针大小的整数倍,访问才有效率),还是时间复杂度(一次和四次的区别,而且这四次有三次都是不整齐的地址),都会低一些。
代码4:
#include<stdio.h>
typedef union
{
char c[4];
int i;
}Union_Typedef;
int main(){
Union_Typedef union_Typedef;
union_Typedef.c[0] = 0x04;//因为是char类型,数字不要太大,算算ascii的范围~
union_Typedef.c[1] = 0x03;//写成16进制为了方便直接打印内存中的值对比
union_Typedef.c[2] = 0x02;
union_Typedef.c[3] = 0x01;
//数组中下标低的,地址也低,按地址从低到高,内存内容依次为:04,03,02,01。总共四字节!
//而把四个字节作为一个整体(不分类型,直接打印十六进制),应该从内存高地址到低地址看,0x01020304,低位04放在低地址上。
printf("%x\n",union_Typedef.i);
}
运行结果:
1020304
因此我的64位Windows是小端(little-endian)。
4.联合体union所占内存空间大小:
union的首地址是固定的,内存所占大小等于union成员中内存所占最大的变量内存。
代码5:
#include<stdio.h>
typedef union
{
char c[4];
int i;
}Union_Typedef;
int main(){
Union_Typedef union_Typedef1;
printf("The union_Typedef1 size:%d\n",sizeof(union_Typedef1));
}
运行结果:
The union_Typedef1 size:4
由此看出一个union_Typedef1所占内存大小就是int类型变量所占内存大小。
这些只是union的一些基础的用法,但是复杂的用法都是建立在此之上的,union中可以有struct,类似的struct中也可以有union,这些用法在日后使用中若遇到问题再做补充总结。