博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
联合体union
阅读量:2190 次
发布时间:2019-05-02

本文共 2796 字,大约阅读时间需要 9 分钟。

当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的:

     1)联合体是一个结构;

     2)它的所有成员相对于基地址的偏移量都为0;

     3)此结构空间要大到足够容纳最"宽"的成员;

     4)其对齐方式要适合其中所有的成员;

下面解释这四条描述:

     由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。

下面举例说明:

如联合体

union
U
{
    
char
s[9];
    
int
n;
    
double
d;
};

s占9字节,n占4字节,d占8字节,因此其至少需9字节的空间。然而其实际大小并不是9,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,9既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。

测试程序:

#include 
typedef union U1{ char s[9]; int n; double d;} U1;typedef union U2{ char s[5]; int n; double d;} U2;int main( int argc, char *argv[] ){ U1 u1; U2 u2; printf( "%d\n", sizeof( u1 ) ); printf( "%d\n", sizeof( u2 ) ); printf( "0x%x\n", ( unsigned int )&u1 ); printf( "0x%x\n", ( unsigned int )&u1.s ); printf( "0x%x\n", ( unsigned int )&u1.n ); printf( "0x%x\n", ( unsigned int )&u1.d ); u1.n = 1; printf( "%d\n", u1.s[0] ); printf( "%lf\n", u1.d ); unsigned char *p = ( unsigned char * )&u1; printf( "%d\n", *p ); printf( "%d\n", *( p + 1 ) ); printf( "%d\n", *( p + 2 ) ); printf( "%d\n", *( p + 3 ) ); printf( "%d\n", *( p + 4 ) ); printf( "%d\n", *( p + 5 ) ); printf( "%d\n", *( p + 6 ) ); printf( "%d\n", *( p + 7 ) ); return 0;}
输出结果为:

16

8
0x22ff60
0x22ff60
0x22ff60
0x22ff60
1
0.000000
1
0
0
0
48
204
64
0
在linux上,sizeof( u1 )为12,因为linux上默认4字节对齐。

对于sizeof(u1)=16。因为u1中s占9字节,n占4字节,d占8字节,因此至少需要9字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u1所占空间的大小能被1,4,8整除,则需填充字节以到16,因此sizeof(u1)=16.

对于sizeof(u2)=8。因为u2中s占5字节,n占4字节,d占8字节,因此至少需要8字节。其包含的基本数据类型为char,int,double分别占1,4,8字节,为了使u2所占空间的大小能被1,4,8整除,不需填充字节,因为8本身就能满足要求。因此sizeof(u2)=8。

从打印出的每个成员的基地址可以看出,联合体中每个成员的基地址都相同,等于联合体变量的首地址。

对u1.n=1,将u1的n赋值为1后,则该段内存的前4个字节存储的数据为00000001 00000000 00000000 00000000

因此取s[0]的数据表示取第一个单元的数据,其整型值为1,所以打印出的结果为1.

至于打印出的d为0.000000原因如下。由于已知该段内存前4字节的单元存储的数据为00000001 00000000 00000000 00000000,从上面打印结果48,204,64,0可以知道后面4个字节单元中的数据为00110000 11001100 01000000 00000000,因此其表示的二进 制浮点数为

00000000 01000000 11001100 00110000 00000000 00000000 00000000 00000001

对于double型数据,第63位0为符号位,62-52 00000000100为阶码,0000 11001100 00110000 00000000 00000000 00000000 00000001为尾数,根据其值知道尾数值约为0,而阶码为4-1023=-1019,因此其表示的浮点数为1.0*2^(-1019)=0.00000000000......,因此输出结果为0.000000。

有一种这样的用法,在C语言中不被支持,但在C++中是支持的:

#include 
int main( int argc, char *argv[] ){ union { char c; int i; }; i = 2; printf( "c: %d\n", c ); return 0;}
结果:

c: 2

看下面的程序运行结果是什么?

#include 
int main( int argc, char *argv[] ){ union { char c; int i; }; c = 3; printf( "i: %d\n", i ); return 0;}
结果是不确定的

转载地址:http://umkub.baihongyu.com/

你可能感兴趣的文章
CART 分类与回归树
查看>>
seq2seq 的 keras 实现
查看>>
seq2seq 入门
查看>>
什么是 Dropout
查看>>
用 LSTM 做时间序列预测的一个小例子
查看>>
用 LSTM 来做一个分类小问题
查看>>
详解 LSTM
查看>>
按时间轴简述九大卷积神经网络
查看>>
详解循环神经网络(Recurrent Neural Network)
查看>>
为什么要用交叉验证
查看>>
用学习曲线 learning curve 来判别过拟合问题
查看>>
用验证曲线 validation curve 选择超参数
查看>>
用 Grid Search 对 SVM 进行调参
查看>>
用 Pipeline 将训练集参数重复应用到测试集
查看>>
PCA 的数学原理和可视化效果
查看>>
机器学习中常用评估指标汇总
查看>>
什么是 ROC AUC
查看>>
Bagging 简述
查看>>
详解 Stacking 的 python 实现
查看>>
简述极大似然估计
查看>>