联合体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各个位域的效果。

经验分享 程序员 微信小程序 职场和发展