联合体(共用体)详解

举报
YIN_尹 发表于 2023/08/07 18:56:16 2023/08/07
【摘要】 1. 联合体(共用体)联合体也是一种特殊的自定义类型1.1 联合类型的定义那联合体要怎么定义呢?与结构体一样,联合类型也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。举个例子:union Un{ char c; int i;};int main(){ //联合变量的定义 union Un un; //计算联合变量的大小 printf...

1. 联合体(共用体)

联合体也是一种特殊的自定义类型


1.1 联合类型的定义

那联合体要怎么定义呢?


与结构体一样,联合类型也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。


举个例子:

union Un
{
    char c;
    int i;
};
int main()
{
    //联合变量的定义
    union Un un;
    //计算联合变量的大小
    printf("%d\n", sizeof(un));
    return 0;
}

2.2联合的特点

那现在大家来思考一个问题,上面的联合体变量un的大小是多少?

在这里要注意上面的一句话:这些成员公用同一块空间(所以联合也叫共用体)

联合体的成员共用一块空间。

那我们来看一下它的大小到底是多大?

2a7995907a144839803697117e4daba8.png

为什么是4个字节呢?


联合体un只有两个成员,char c; int i;c 大小1个字节,i是最大的成员4个字节。

那么因为它们共用同一块空间,所以四个字节就够了。

4个字节既可以放得下c ,也能放得下i。


所以:


联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。


那怎么证明它们是共用同一块空间的呢?

union Un
{
    int i;
    char c;
};
int main()
{
    union Un un;
    // 下面输出的结果是一样的吗?
    printf("%p\n", &(un.i));
    printf("%p\n", &(un.c));
    return 0;
}

我们是不是可以看一下它们的地址,如果共用一块空间,那地址肯定是一样的,对吧。

我们来看一下:

91648e6deb2d439ea36ac6558a5de785.png

我们打印出来成员i和c的地址,是一样的!!!

注:我们看到联合体访问成员和结构体的方式是一样的。(un.i)

那他们共用一块内存空间意味着什么呢?

是不是意味着它们不能同时存在啊,或者说它们不能同时使用。

就拿上面的哪个union Un来说:

ffb919957a1a4b05b9db5672285139ee.png

当我们使用成员 i 时,对于c来说,此时c的那一个字节里面是不是存的是 i 的内容,就相当于此时c是不存在的。

来看一段代码:

    un.i = 0x11223344;
    un.c = 0x55;
    printf("%x\n", un.i);

因为它们共用一块空间,上述代码中先给un.i赋值。

那此时un的四个字节的空间里放的应该是这样的内容:

假设左边低地址,右边高地址

a8e34f69c5b3448eac2802749a9c6202.png那我们在去给un.c赋值,是不是就会改变1个字节的内容(因为un.c是1个字节)。

应该就变成这样了:


20bc6a8310b44de1b9d2287f46c07b15.png

那我们再以printf("%x\n", un.i);(%x是以16进制形式打印)打印出来是不是就是11223355了。

来验证一下:

5c68b3f04e344daba3bb1982ee106e80.png

这样更好的验证了联合体的成员共用一块空间,不能同时存在。

2.3使用联合体解求机器字节序的问题

那接下来我们就使用联合体来解决一道题:

在之前的一篇文章——深度剖析整形数据在内存中的存储

中讲解过一道题:

94257e902ccc498894ff4bdbc1db6589.png

大家如果忘了字节序相关的知识可以去复习一下。

当时我们是这样解的:

#include <stdio.h>
int main()
{
    int a = 1;
    if ((*(char*)&a) == 1)
        printf("小端");
    else
        printf("大端");
    return 0;
}

94f5657aef5c4c70834a67f309ef80d0.png

现在还是同样的思路,我们换一种方法,借助联合体解这道题:

int check_sys()
{
    union un
    {
        int i;
        char c;
    }u;
    u.i = 1;
    return u.c;
}
int main()
{
    int ret = check_sys();
    if (ret == 1)
        printf("小端");
    else
        printf("大端");
    return 0;
}

解释一下:

首先我们知道 i, c肯定占用的是同一块空间:

f152694d22d94cfc8cfcbb5b7f6c140d.png

u.i = 1;执行之后,联合体u的4个字节里放的应该是

a36cea6c7b2548b39c5aa7421c2216d7.png

然后我们并没有给u.c赋值,而是直接返回u.c的值,那u.c只占1个字节,但是还是上面那四个字节的空间,所以返回的就是1,结果就应该是小端。

我们看一下结果:

1da393fa2b8d40bda7fd61c29c206b5e.png

2.4 联合体大小的计算

在联合体的特点里我们已经知道:


一个联合体的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)

我们在上面举的例子中的联合体大小就是它的最大成员的大小,但是不是所有的联合体大小都是这样呢?

不是的,联合体大小的计算其实也要考虑对齐的。


当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

举个例子:

#include <stdio.h>
union Un1
{
    char c[5];
    int i;
};
int main()
{
    printf("%d", sizeof(union Un1));
    return 0;
}

参照上面的规则,大家思考union Un1的大小是多少?

是最大成员的大小,5个字节吗?

cb98fd427ae0461daa5471a6e4dfcd77.png

是8个字节哎!为什么呢?


我们一起来算一下:


首先最大成员的大小是几?

是不是5啊,最大的成员应该是char c[5];,大小5个字节,那按照上面第二条规则,我们是不是要判断一下5是不是最大对齐数的整数倍啊。

首先char c[5];,这里要注意,它是一个数组,计算它的的对齐数的时候比的是数组一个元素的大小和默认对齐数,去它们之中的较小值。

那char c[5];的类型是char,每个元素1个字节,小于8,所以对齐数是1;

另外一个成员int i;对齐数是4,5不是4的整数倍,所以要对齐到4的整数倍,因此union Un1的大小是8个字节。


好了,讲到这里,C语言中的自定义类型就全部学习完了。总共写了3篇文章,欢迎大家指正!!!

我们一起进步!!!

3a52b760d869482e8c2ed102e15c9e47.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。