联合体union的巧妙用法
C语言中有种变量类型叫联合体,即union。在联合体里,各成员共享一段内存空间,一个联合体变量的大小等于各成员中最大的那个元素大小。
下面来看一下联合体的巧妙用法。
例子
假设有一个结构体如下,
struct data {
uint32_t num1;
uint32_t num2;
};
假如struct data里的num1成员的不同位域有特殊意义,例如
-
[0 : 3]位表示椅子的数目 [4 : 7]位表示课桌的数目 [8 : 15]位表示学生的数目 [16 : 23]位表示教师的数目 [24 : 31]位表示班级的数目
那么当我们需要给num1的不同位域填充这些数字,可能需要使用以下方式
struct data testData; testData.num1 = (2 << 24) | (10 << 16) | (5 << 8) | (7 << 4) | (7 << 0);
或者我们需要单独修改其中某些位域,
struct data testData; testData.num1 |= ( (10 << 16) | (7 << 4) );
很明显,这种方式不优雅而且容易出错(记错位域范围)。下面让我们看下联合体如何巧妙的修改num1的各个位域值,假设我们有个联合体如下,
typedef union {
struct data testData;
struct {
uint32_t chairNum : 4;
uint32_t desktopNum : 4;
uint32_t studentNum : 8;
uint32_t teacherNum : 8;
uint32_t classNum : 8;
uint32_t padding;
} config;
} goodData;
config变量里chairNum、desktopNum、studentNum、teacherNum和classNum在内存地址上和testData.num1的各个位域对应,且他们总共占4个字节。这样,我们再去设置num1的各个位域就可以使用如下方式,
goodData ttt; ttt.config.chairNum = 7; ttt.config.desktopNum = 7; ttt.config.studentNum = 5; ttt.config.teacherNum = 10; ttt.config.classNum = 2;
整个联合体实际占的内存空间和struct data是一样的。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
struct data {
uint32_t num1;
uint32_t num2;
};
typedef union {
struct data testData;
struct {
uint32_t chairNum : 4;
uint32_t desktopNum : 4;
uint32_t studentNum : 8;
uint32_t teacherNum : 8;
uint32_t classNum : 8;
uint32_t padding;
} config;
} goodData;
int main()
{
goodData ttt;
ttt.testData.num1 = 0;
ttt.config.chairNum = 7;
ttt.config.desktopNum = 7;
ttt.config.studentNum = 5;
ttt.config.teacherNum = 10;
ttt.config.classNum = 2;
uint32_t actual = 2 << 24 | 10 << 16 | 5 << 8 | 7 << 4 | 7 << 0; // 用来做比较
printf("num1: 0x%x, actual: 0x%x
", ttt.testData.num1, actual);
return 0;
}
输出如下, 可以看出2个值是一样的,说明修改成功。
总结
这个方法的主要关键点就是要保证config和testData两者之间的内存位置一样,或者至少要保证config所占的内存空间位置要覆盖到testData中的num1,这样两者在内存中就可以对应上,然后修改config中的成员变量值(使用位域)就可以达到修改num1各个位域的效果。
