美文网首页C++
数据结构 -- 共用体Union

数据结构 -- 共用体Union

作者: 东篱采桑人 | 来源:发表于2020-09-17 00:32 被阅读0次

    数据结构 -- 结构体Struct一文中详细介绍了结构体的定义以及内存对齐。在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体Union),也称为联合体。它的定义格式为:

    union 共用体名{
        成员列表
    };
    

    1. 定义共用体变量

    和结构体一样,共用体也是一种自定义的数据类型,是创建变量的模板,不占用内存空间。共用体变量才包含了实实在在的数据,需要内存空间来存储。共用体可以通过下面两种方式来定义:

    • 方式一:先定义共用体,再定义共用体变量
    //定义data共用体
    union data{
        int n;
        char ch;
        double f;
    };
    
    //定义两个共用体变量
    union data a, b;
    

    data为共用体名,里面包含n、ch、f这3个成员。ab则为两个data类型的共用体变量。

    • 方式二:在定义共用体的同时定义共用体变量
    union data{
        int n;
        char ch;
        double f;
    } a, b;
    

    直接将变量放在共用体的最后即可。

    如果只需要 a、b两个变量,后面不需要再使用共用体名定义其他变量,那么在定义时也可以省略共用体名。

    2. 成员的获取和赋值

    共用体使用了内存覆盖机制,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。所以共用体不能整体赋值,只能使用点号.获取单个成员,然后再进行赋值操作。

    //注:下面每一次赋值后,n、ch、f三个变量的值均会改变
    a.n = 40;
    a.ch = '9';
    a.f = 135;
    

    3. 共用体的内存分配

    共用体变量占用的内存大小等于最长的成员占用的内存大小,各成员都会从offset为0处开始存放,修改其中一个成员会影响其余所有成员。来看下面的实例:

    //定义一个data共用体
    union data{
        int n;
        char ch;
        short m;
    };
    
    int main(){
         
        //创建一个共用体变量a
        union data a;
        printf("a.size = %d, data.size = %d\n", sizeof(a), sizeof(union data) );
        //对成员a赋值
        a.n = 0x40;
        printf("n = %#X, ch = %c, m = %#hX\n",  a.n, a.ch, a.m);
        //对成员b赋值
        a.ch = '9';
        printf("n = %#X, ch = %c, m = %#hX\n", a.n, a.ch, a.m);
        //对成员c赋值
        a.m = 0x2059;
        printf("n = %#X, ch = %c, m = %#hX\n", a.n, a.ch, a.m);
       //再对成员a赋值
        a.n = 0x3E25AD54;
        printf("n = %#X, ch = %c, m = %#hX\n", a.n, a.ch, a.m);
       
        return 0;
    }
    

    在上面代码中创建了一个共用体变量a,里面有n、ch、m这三种不同数据类型的成员,分别对齐赋值,打印每一次赋值后的三个成员。

    %X:表示将int类型数据以大写字母的形式输出十六位进制数。
    %c:表示输出字符。
    %hX:表示将short类型数据以大写字母的形式输出十六位进制数。
    #:表示输出时加上前缀,用于区分不同进制的数字。比如:0x16,不加#时输出为16,加上输出为0x16。

    打印结果为:

    a.size = 4, data.size = 4
    n = 0X40, ch = @, m = 0X40
    n = 0X39, ch = 9, m = 0X39
    n = 0X2059, ch = Y, m = 0X2059
    n = 0X3e25ad54, ch = T, m = 0XAD54
    

    从打印结果可知,共用体a的内存等于其最长成员int n所占的内存大小,为4字节。每次修改共用体中的某个成员,都会影响到其他成员的值。

    4. 分析共用体的内存分配

    共用体的每个成员都从其内存offset为0的地方存放,根据成员数据类型各占用对应字节大小的内存,每次为成员赋值时,都会覆盖修改所占内存,因此,其他成员的值也跟着变动了。以上面的共存体变量a为例,分别对其成员赋值,内存空间的变化情况就如下图所示:

    图1 → 图2 → 图3 → 图4 → 图5

    图1:表示共用体a及其成员的内存分配情况:

    • 每一格代表一个字节,从上到下表示地址由低到高分布。
    • a总共4字节,成员n、m、ch均从offset为0的地方开始存放,分别占用4字节、2字节、1字节的内存大小,内存存在重合部分。

    图2:表示当赋值a.n = 0x40后,共用体a的内存空间变化:

    1byte = 8bit,所以1个字节最大值为255,换算成十六进制为0xFF,而0x40小于0xFF,所以只需一个字节保存。存储系统的分布方式大多采用小端模式,即在内存的第一个字节里保存0x40,此时成员n、ch、m的值均为0x40。

    存储系统分布方式,以0x12345678为例:
    1.大端模式:高位在低地址,低位在高地址。即12在低地址,78在高地址。
    2.小端模式:高位在高地址,低位在低地址。即12在高地址,78在低地址。

    因此,当赋值a.n = 0x40后,打印结果为:n = 0X40, ch = @, m = 0X40。(0x40对应的字符为@

    图3:表示当赋值a.ch = '9'后,共用体a的内存空间变化:

    字符9的十六进制ASCII码值为0x39,在赋值后,内存里第一个字节的0x40被覆盖,变为0x39。

    因此当赋值a.ch = '9'后,打印结果为:n = 0X39, ch = 9, m = 0X39。

    图4:表示当赋值a.m = 0x2059后,共用体a的内存空间变化:

    由于0x2059小于0xFFFF,所以需要两个字节来保存,在赋值后,内存里第一个字节里的39被覆盖,前两个字节分别保存0x59、0x20。

    因此当赋值a.m = 0x2059后,ch为0x59,对应字符Y,所以打印结果为:n = 0X2059, ch = Y, m = 0X2059。

    图5:表示当赋值a.n = 0x3E25AD54后,共用体a的内存空间变化:

    由于0x3E25AD54小于0xFFFFFFFF,所以需要4字节来保存。在赋值后,内存的4个字节分别保存54、AD、25、3E。

    因此当赋值a.n = 0x3E25AD54后,ch为0x54,对应字符T,m为0xAD54,所以打印结果为:n = 0X3E25AD54, ch = T, m = 0XAD54。

    5. 共用体和联合体的区别
    • 结构体的第一个成员会从offset为0的地方开始存放,其它成员按顺序存储,各成员会占用不同的内存,互相之间没有影响;
      共用体的所有成员都会从offset为0的地方存放,各成员内存会存在重,修改一个成员会影响其余所有成员。

    • 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙)。
      共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

    推荐阅读

    1. 数据结构 -- 结构体Struct
    2. 数据结构 -- 位域

    相关文章

      网友评论

        本文标题:数据结构 -- 共用体Union

        本文链接:https://www.haomeiwen.com/subject/aelkyktx.html