LV100-共用体
一、共用体简介
1. 什么是共用体
在 C 语言中,不同数据类型的数据可以 使用共同的存储区域,这种数据构造类型称为共用体,简称 共用,又称 联合体。共用体在定义、说明和使用形式上与结构体相似。两者本质上的不同仅在于使用内存的方式上。
2. 怎么定义?
定义一个共用体数据类型,我们需要用到 union 关键字:
union union_name
{
data_type member_name1;
data_type member_name2;
...
data_type member_nameN;
};各部分说明如下:
| union | 共用体的关键字,表明这是一个共用体的定义 |
| union_name | 共用体的名称,与普通变量名要求一样 |
| data_type | 共用体内成员的数据类型,可以是基本变量类型,指针类型,枚举类型,联合体类型或者结构体类型等。比如,char,int 等 |
| member_nameN | 共用体内成员的名称(成员变量名),命名规则与变量相同 |
(1)共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
(2)共用体 占用的内存等于最长的成员占用的内存。
(3)共用体使用了内存覆盖技术, 同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
二、共用体的长度
1. 怎么计算?
我们可以使用 sizeof 函数来求得所定义共用体的长度。共用体既然共用同一段内存区域,那么它的长度就应该 等于共用体成员中最长的数据类型的长度。
2. 使用实例
#include <stdio.h>
/* 定义共用体数据类型 */
union data1
{
char a;
short b;
int c;
};
union data2
{
char a;
short b;
};
int main(int argc, char *argv[])
{
union data1 temp1; /* 定义共用体变量 */
union data2 temp2; /* 定义共用体变量 */
printf("sizeof(temp1) = %ld\n", sizeof(temp1));
printf("sizeof(union data1) = %ld\n\n", sizeof(union data1));
printf("sizeof(temp2) = %ld\n", sizeof(temp2));
printf("sizeof(union data2) = %ld\n", sizeof(union data2));
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
sizeof(temp1) = 4
sizeof(union data1) = 4
sizeof(temp2) = 2
sizeof(union data2) = 2我们会发现(我用的 linux 是 64 位的),总的大小是等于最长的数据所占空间大小的。
三、在内存中的存储
既然共用体的成员共用一段内存空间,那打印地址的话,他们怎么分布呢?
#include <stdio.h>
/* 定义共用体数据类型 */
union data
{
char a;
short b;
int c;
};
int main(int argc, char *argv[])
{
union data temp; /* 定义共用体变量 */
printf("&temp = %p\n", &temp);
printf("&temp.a = %p\n", &temp.a);
printf("&temp.b = %p\n", &temp.b);
printf("&temp.c = %p\n", &temp.c);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
sizeof(temp) = 4
sizeof(union data) = 4
&temp = 0x7ffc7c3d2b14
&temp.a = 0x7ffc7c3d2b14
&temp.b = 0x7ffc7c3d2b14
&temp.c = 0x7ffc7c3d2b14从例子中不难看出,这几个成员的地址都是一样的,他们确实共用同一段内存。
四、怎么赋值?
看到这里,我会想,它跟结构体用法一样,但是却又共用同一段内存,那我还能给它赋值嘛?接下来我们就开看一看成员赋值时的变化情况。
1. 测试示例
#include <stdio.h>
/* 定义共用体数据类型 */
union data
{
char a;
short b;
int c;
};
int main(int argc, char *argv[])
{
union data temp; /* 定义共用体变量 */
printf("sizeof(temp) = %ld\n", sizeof(temp));
printf("sizeof(union data) = %ld\n\n", sizeof(union data));
printf("&temp.a = %p,&temp.b = %p,&temp.b = %p\n", &temp.a, &temp.b, &temp.c);
printf("temp.a\t\ttemp.b\t\ttemp.c\n");
printf("%c(%#x)\t%#hx\t\t%d(%#x)\n", temp.a, temp.a, temp.b, temp.c, temp.c);
temp.c = 0x40;
printf("%c(%#x)\t\t%#hx\t\t%d(%#x)\n", temp.a, temp.a, temp.b, temp.c, temp.c);
temp.a = '9';
printf("%c(%#x)\t\t%#hx\t\t%d(%#x)\n", temp.a, temp.a, temp.b, temp.c, temp.c);
temp.b = 0x2059;
printf("%c(%#x)\t\t%#hx\t\t%d(%#x)\n", temp.a, temp.a, temp.b, temp.c, temp.c);
temp.c = 0x3e25ad54;
printf("%c(%#x)\t\t%#hx\t\t%d(%#x)\n", temp.a, temp.a, temp.b, temp.c, temp.c);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
sizeof(temp) = 4
sizeof(union data) = 4
&temp.a = 0x7ffc18626194,&temp.b = 0x7ffc18626194,&temp.b = 0x7ffc18626194
temp.a temp.b temp.c
�(0xfffffffc) 0x7ffc 32764(0x7ffc)
@(0x40) 0x40 64(0x40)
9(0x39) 0x39 57(0x39)
Y(0x59) 0x2059 8281(0x2059)
T(0x54) 0xad54 1042656596(0x3e25ad54)为什么要先对整型成员赋值呢?这是因为整型成员赋值后可以将这一个字节都给初始化掉,未初始化的时候谁也不知道里边是什么,这也是为了保证结果的准确性。接下来我们来分析一下数据的变化情况。
2. 结果分析
union data
{
char a;
short b;
int c;
};【说明】红色字体为每次变化的位置。
- temp.c = 0x40; 赋值后
| 0x7ffc18626194 | 0x7ffc18626195 | 0x7ffc18626196 | 0x7ffc18626197 |
| 0x40 | 0x00 | 0x00 | 0x00 |
| temp.a = 0x40 | |||
| temp.b = 0x40 | |||
| temp.c = 0x40 | |||
- temp.a = '9'; 赋值后
| 0x7ffc18626194 | 0x7ffc18626195 | 0x7ffc18626196 | 0x7ffc18626197 |
| 0x39 | 0x00 | 0x00 | 0x00 |
| temp.a = '9' (ASCII 为 0x39) | |||
| temp.b = 0x39 | |||
| temp.c = 0x39 | |||
- temp.b = 0x2059; 赋值后
| 0x7ffc18626194 | 0x7ffc18626195 | 0x7ffc18626196 | 0x7ffc18626197 |
| 0x59 | 0x20 | 0x00 | 0x00 |
| temp.a = 0x59 | |||
| temp.b = 0x2059 | |||
| temp.c = 0x39 | |||
- temp.c = 0x3e25ad54; 赋值后
| 0x7ffc18626194 | 0x7ffc18626195 | 0x7ffc18626196 | 0x7ffc18626197 |
| 0x54 | 0xad | 0x25 | 0x3e |
| temp.a = 0x54 | |||
| temp.b = 0xad54 | |||
| temp.c = 0x3e25ad54 | |||
成员 a 、 b 、 c 在内存中“对齐”到一头,对 a 赋值修改的是前一个字节,对 b 赋值修改的是前两个字节,对 c 赋值修改的是全部字节。也就是说, a 、 b 会影响到 c 的一部分数据,而 c 会影响到 a 、 b 的全部数据。
根据 temp.a 、 temp.b 和 temp.c 的分析,可以得出数据在内存中的存储形式。按照最后一次整型的赋值,我们再通过根据 temp.a 、 temp.b 的值,就可以分析出这四个字节的数据的存储是符合小端模式的,高字节存储在高地址,低字节存储在低位,同时这也是一种 判定大小端 的方式(大小端可以看后边的关于大小端的笔记)。