共用体
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.