c++的 union类型平时用的真心少 从来没有认真研究过他
今天看了几道c++的笔试题 有几道 关于union的 题目 现在总结下
第一题:
union V { struct X { unsigned char s1:2; unsigned char s2:3; unsigned char s3:3; } x; unsigned char c; } v; v.c = 100;
printf("%d", v.x.s3);
网上的解释
v是联合体(共用体)变量,共有两个元素x和c,都需要一个字节,它们分配于同一个地址。
而x是结构体变量,共有三个元素s1、s2、s3,分别占2位、3位、3位。分配内存时低位在前,最位在后。
当有v.c=100(其二进制为01100100)时,各变量的关系及内存存储情况见图所示。
其中x的成员s3为二进制的011,即十进制的3,所以输出结果为3。
同理的第二题:
#i nclude <stdio.h> union { int i; char x[2]; }a; void main() { a.x[0] = 10; a.x[1] = 1; printf("%d",a.i); }int 为 .. ... 0000 0001 0000 0010
答案:266 (低位低地址,高位高地址,内存占用情况是Ox010A)
学习中 看了些文章 有所收获 下面转两篇文章
一篇是 union和struct的 大小问题
1,对于union,对齐的大小是最大的基本元素的对齐大小;对象的大小必须是该基本元素大小的整数倍;
2,对于struct,对齐的大小也是最大的基本元素的对齐大小,对象的大小需要考虑元素的对齐,并且需要是最大基本元素的整数倍;同时有#pragma pack修饰的情况,关于struct请详细参考另外一个帖子。
3,这里所说的struct和union的对齐,是指其作为其他复杂对象中的元素的时候要求的对齐,对于本身大小的计算并没有关系。本身的大小只和其所包含的基本元素的对齐有关系。
(copy 一段网上的对齐规则 1,数据成员对齐规则:结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储) 2,,结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有 stuct b, b里有char,int,double等元素,那么b应该从8的整数倍开始存储) 3,收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。)
例子一:
union U1
{
char a[9]; //对齐大小是1,大小是9个字节
int b; //对齐大小是4,大小是4个字节
};
所以该union的对齐大小是4个字节;大小为大于等于max(9,4)=9并为4的整数倍,所以是12字节。
例子二:
union U1_Another
{
char a[9]; //对齐大小是1,大小是9个字节
double b; //对齐大小是8,大小是8个字节
};
所以该union的对齐大小是8个字节,大小是大于等于9并为8的整数倍,即为16字节;
例子三:
union U2
{
U1 a; //对齐大小是4个字节,大小是12字节
double b; //对齐大小是8个字节,大小是8字节
};
所以该union的对齐大小是8个字节,大小是16;
例子四:
struct S1
{
char a[13]; //对齐大小是1字节,位置是[0,12]
double c; //对齐大小是8字节,位置是[16,23]
}
所以该结构体是8字节对齐,大小为24个字节;
一.union数据类型
union顾名思义,联合体,float,int,char等等类型数据共用一块内存空间。其大小为占据内存空间最大的那个变量的空间大小。
eg:
union
{
float a;
char b[2];
}u_test;
那么union所占的内存空间大小为float类型变量a所占的内存大小,在常见32位计算机上为4字节。
二.大小端对union的影响
一般x86体系的计算机采用的是小端模式:高高低低,高字节数据存放在内存中的高地址处,低字节数据存放在内存中的低地址处。
反之,大端模式就是高低高低了,高字节数据存放在内存中的低地址处,低字节数据存放在内存中的高地址处。
16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 |
小端模式存放内容 |
大端模式存放内容 |
0x4000 |
0x34 |
0x12 |
0x4001 |
0x12 |
0x34 |
说明:内存中最小的单位为1字节(8bit),上表中的0x4000正是这样一个字节内存,显然相对于0x4001而言,0x4000是低地址。低字节数据0x34就存放于内存的低地址0x4000处,而高字节数据0x12就存放于内存的高地址0x4001处。
32bit宽的数0x12345678在Little-endian模式以及Big-endian模式)CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 |
小端模式存放内容 |
大端模式存放内容 |
0x4000 |
0x78 |
0x12 |
0x4001 |
0x56 |
0x34 |
0x4002 |
0x34 |
0x56 |
0x4003 |
0x12 |
0x78 |
三.测试大小端对union的影响,假设union分配的内存地址首地址为0,
eg:
union u_test
{
int a;
char b[2];
}mm;
写一个测试函数:
void test_union(void)
{
mm.a = 65*256 + 97;
printf("union's b[0] is: %c,b[1] is: %c\n",mm.b[0],mm.b[1]);
}
这个结构体的大小为int a的大小4字节。假设在内存中是从地址0开始的连续4个单位。显然这4个字节的内存块的存放着a的值。则按照高高低低的原则,低内存地址0应该存放的是低字节数据97,依此往下存放,即内存地址为1的内存单元存放着十进制数为65的数据,内存地址为2的内存单元存放着0,内存地址为3的内存单元存放着0。
内存地址 |
小端模式存放内容 |
0 |
存放着十进制97 |
1 |
存放着十进制65 |
2 |
0 |
3 |
0 |
所以上面的函数test_union执行的结果将会是打印小写字母a(97)和大写字母A(65).
那如果是大端模式将会是怎样的结果呢?如下:
内存地址 |
大端模式存放内容 |
0 |
0 |
1 |
0 |
2 |
十进制65 |
3 |
十进制97 |
四.测试CPU是大端还是小端模式?
当然对于一般的CPU,我们已经了解到:
Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式。
如果不知平台到底运行于何种模式,也根据上面的这些特性可以自己测试出来?
注: union 为低地址对齐 如果是 little 则int 为 01 00 00 00 然后 j 对齐低地址 为01 所以c.j==1
如果为big为 00 00 00 01 然后j对齐低地址 为 00 所以c.j==0
或者:
bool IsBigendian() { unsigned short usData = 0x1122; unsigned char *pucData = (unsigned char*)&usData; return (*pucData == 0x11); }注: 转换 也是从地地址开始处理 若为 little usData 存为 22 11 pucData从低地址开始获取 则为 22
若为 big usData存为 11 22 pucData从低地址开始获取 则为 11