构造类型 -- 共用体

共用体

1、产生及意义

结构体与共用体相比,最大的特点是成员属于合作关系。内存空间大小是按照结构体成员定义顺序来分配的。但是,共用体则不同,共用体中的成员是敌我关系。同一时刻,只能有一个成员生效。根据当前成员所占内存最大的那个来分配空间大小。

结构体成员在同一时刻可以共存。(爱好可以有多种)
而共用体成员则同一时刻只能有一个存在。(比如要填性别信息,只能填男或女)

共用体产生的背景与过去的一段历史发展时期有关,在这段时期中,硬件的发展跟不上软件的发展,因此,有时候内存空间不够用,需要节省空间,从而共用体这种数据类型就出现了。

2、类型描述

union 共用体名
{
	数据类型 成员名1;
	数据类型 成员名2;
	......	
};

3、嵌套定义

共用体嵌套定义结构体或共用体嵌套定义共用体。

很多时候,我们利用他们在存储上的特点来把一些操作来进行简化,见下面的示例程序。

4、定义变量,初始化及成员引用

如果我们只给其中一个成员变量初始化,却使用另一个成员变量的值,那么这是没有任何意义的。(因为其他值相对你来说是隐形的,是不生效的)

#include <stdio.h>
#include <stdlib.h>

union test
{
    int i;
    float f;
    double d;
    char ch;
};

int main()
{
    union test a;

    a.f = 3.1415;

    printf("%lf.\n",a.f);

//  打印没使用到的成员变量的值,这种做法是没有意义的
//  printf("%d.\n",a.i);
    printf("%d\n",sizeof(a));

    return 0;
}

成员引用:

1、变量名.成员名
2、指针名->成员名

5、所占用的内存大小
在上述代码中,我们知道float数据类型在32位机器和64位机器中都占4个字节。但共用体是根据当前成员所占内存最大的那个来分配空间,也就是double类型。所以输出结果是8。

6、关于函数传参的问题(值传递、地址传递)

示例:
现在有个需求,我有一个32位的无符号整型的数,我想知道高16位和低16位相加的和是多少?

实现方法一:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{
    uint32_t i = 0x11223344;

    // i>>16    //得到高位
    // i&0xFFFF //得到低位

    //以16进制输出
    printf("0x%x.\n",((i >>16) + (i & 0xFFFF)));

    return 0;
}

利用结构体和共用体在存储上的特点来把一些操作进行简化,见下面的示例程序。

实现方法二:使用构造类型

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

union
{
    //凑起来刚好是四个字节
    struct
    {
        //i和j紧挨着
        uint16_t i;
        uint16_t j;
    }x;

    //与y存放的空间一致
    uint32_t y;
}a;

int main()
{
    a.y = 0x11223344;

    //以16进制输出
    //虽然存放的是y,但是我可以这样操作.
    printf("0x%x.\n",a.x.i+a.x.j);

    return 0;
}

程序运行输出:
在这里插入图片描述
7、位域

没有实际开发上的意义,但是面试有可能会考到。

位域是一种利用共用体来存储变量的形式,但是他来存放变量时,可能就不以字节为单位,而是以位为单位。

我们通过例子的方式来看一下。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

union
{
    struct
    {
        //定义一个char型的a,占1位
        char a:1;
        //定义一个char型的b,占2位
        char b:2;
        //我想用某个变量来表示某种状态。
        //可能它只有两种状态,我就完全没
        //有必要使用8个位(1个字节)来表示。
    }x;
        //看上去虽然很省空间,但是没有考虑到具体的硬件平台,
        //不同的硬件平台的存储方式分为大端模式和小端模式,
        //也就是说位域不利于移植.

    char y;
}w;

int main()
{
    w.y = 1;
    
    //虽然存放的是y,但是我可以这样操作.
    printf("%d.\n",w.x.a);

    return 0;
}

当前系统采用小端模式(高字节保存在高位,低字节保存在低位),请问此时打印出来的值为多少?
在这里插入图片描述
那为什么是-1呢?
我们来分析下原因。
在这里插入图片描述
我们使用 %d 来输出,%d输出的是有符号整数。

printf("%d.\n",w.x.a);

我们知道,计算机中的数使用补码表示的。
而补码的表示方法是:正数的补码就是其本身。
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

这里比较绕,但针对这个问题来说,我们说的a=0或1、b=00、01、10或11都是内存中的值,内存中的值是以补码形式存在的,而补码的最高位又既表示符号也表示数值的。
在这里插入图片描述
当a为0时,表示正数,数值位(也是符号位)为0,直接输出0。
当a为1时,表示负数,数值位(也是符号位)是1,输出时要还原为原码之后再输出出来。怎么还原呢?数值位的1先减1,再取反,仍就变成了1。(因为是负数),所以输出出来的结果是 -1。

小端模式,最高(高字节)位为符号位。

那么,以此类推。
当程序中

w.y = 2;

在这里插入图片描述
我们再来使用 %d 来输出,输出的结果又是多少?

printf("%d.\n",w.x.b);

当b为00时,最高位0为符号位,表示正数,数值位为00,直接输出0。
当b为01时,最高位0为符号位,表示正数,数值位为01,输出出来的结果是 1。
在这里插入图片描述
当程序中

w.y = 4;

在这里插入图片描述
b为10时,最高位的1为符号位,因此表示负数,输出时要还原为原码之后再输出出来。怎么还原呢?数值位为10先减1,变成01,再取反,也就变成了10。因为是负数,所以输出-2.
在这里插入图片描述
当程序中

w.y = 6;//或者7时

也就是b为11时,最高位的1为符号位,因此表示负数,输出时要还原为原码之后再输出出来。怎么还原呢?数值位为11先减1,变成10,再取反,也就变成了01。因为是负数,所以输出-1.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuechanba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值