数据结构 -- 共用体Union

数据结构 -- 结构体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. 数据结构 -- 位域

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351