C语言中的共用体(联合体)union

在定义结构体和共用体时,都是{}里面写成员,都是自定义复合类型,都可以嵌套、都可以做数组、指针。上节我们重点分析了结构体,本文将主要讲共用体。
一、定义
共用体是一种自定义数据类型,多个成员共享同一块空间。
同一时刻,只有一个成员是有效的;给一个成员赋值,会覆盖其他成员的值。
共用体占用的内存大小:等于它最大成员所占的字节数。

#include<stdio.h>
#include<windows.h>
#include<math.h>

union data{
    int i;
    char c;
    float f;
};

int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    union data d;
    printf("共用体data占:%d字节\n",sizeof(d));
    d.i=0x68;
    printf("i=%lx,c=%c\n",d.i,d.c);
    d.f=3.14f;//覆盖整个内存
    printf("f=%f,i=%lx\n",d.f,d.i);    
    return 0;
}

运行结果:
在这里插入图片描述
赋值完d.f之后,再读d.i属于类型双关,C标准未定义这种行为,只是大部分编译器可以跑。
对于值的改变,我们通过下面这个例子可以清晰的看到,后面成员变量的赋值,直接把原来的值进行了部分覆盖:

#include<stdio.h>
#include<windows.h>
#include<math.h>

union data{
    int a;
    short b;
};
int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    union data s1;
    s1.a =0x01020304;
    s1.b =0x0102;

    printf("a=0x%X\n",s1.a);  
    return 0;
} 

运行结果:
在这里插入图片描述
二、使用场景
1、类型双关
把同一块内存,以不同类型视角解读。eg:把float二进制位当作整数查看

union u{
    float ff;
    unsigned int ii;
};
int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    union u test;
    test.ff=1.0f;
    printf("ff=%f,ii=%lx\n",test.ff,test.ii);   
    return 0;
}

运行结果:
在这里插入图片描述
2、节省空间
多个变量不会同时使用,放在union共用一块内存,减少内存占用。

struct mag{
    int type;
    union data{
        int ival;
        float fval;
        char cval;
    };
};

例如:type=0,可以代表ival,type=1,代表fval;type=2代表cval
Msg结构体必须带type标记,工程上用union一定要加标记位,否则不知道哪个成员有效。
(3)硬件、网络协议、二进制报文解析
协议字段中,同一个位置,根据标志位不同代表不同含义,用union非常适合
(4)寄存器操作(嵌入式)
硬件寄存器,既可以整体读写,也可以按位/字节访问,使union变量互斥不同时使用,需要同一块内存做多种类型解释,内存紧张(嵌入式)
三、注意事项
1、同一时刻只有一个成员有效**(容易忽视,再重复)**
2、大小是最大成员大小,但是考虑内存对齐

union u{
	char arr[5];
	int a;
}

上面最大成员是5字节,但是对齐是按照int(4),sizeof(U)=8,不是5
3、不要混用指针
不要拿union不同成员的指针随意强制转化,容易对齐问题
4、联合体不能直接整体比较if(u1==u2),C语言不支持,需要自己逐个成员对比
5、大小端问题
CPU内部寄存器是按照bit取存储数据,但是CPU内部寄存器资源有限,需要把计算好的数据放到内存中,对于内存来说,它没有bit位的概念,是按照字节编号(地址)来寻址。把寄存器中数据存到内存,按照哪种顺序保存就是大小端的问题。
小端模式:数据的高位存到内存的高地址,数据的低位到内存的低地址(现阶段使用小端的比较多)。
大端模式:数据的高位存到内存的低地址,数据的低位存到内存的高地址。

用union拆分字节看底层二进制,程序受CPU大小端影响,跨平台代码要小心。
判断大小端的方法:

#include<stdio.h>
#include<windows.h>
#include<math.h>
union EndianCheck{
    unsigned int val;
    unsigned char bytes[4];
};

int main(){
    SetConsoleOutputCP(65001);
    SetConsoleCP(65001);
    union EndianCheck test;
    test.val =0x01020304;
    printf("val=0x%08X\n",test.val);
    printf("bytes[0]=0x%02x bytes[1]=0x%02x bytes[2]=0x%02x bytes[3]=0x%02x\n",test.bytes[0],test.bytes[1],test.bytes[2],test.bytes[3]);
    //对于寄存器,右边是低位;对内存,左边是低地址。
    if(test.bytes[0]==0x04){
        printf("本机:小端模式\n");
    }
    else{
        printf("本机:大端模式\n");
    }   
    return 0;
} 

本机运行结果:
在这里插入图片描述
四、结构体和共用体的区别
口诀:
结构体:各占各的,全部共存,内存相加(对齐后)
共用体:抢占同步一块,互斥共存,取最大(对齐后)

特性struct结构体union共用体
内存布局每个成员独立分配内存所有成员共享同一块内存
总大小=全部成员总和=最大成员大小(对齐后)
生命周期全部成员同时有效同一时刻只有一个成员有效
用途存放一组同时存在的属性互斥数据,类型双关,省内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值