一、定义:
union维护足够的空间来放置多个数据成员中的一种,而不是为每一个数据成员配置空间。在union中所有的数据成员公用一个空间,同一时间只能存储其中一个数据成员,并且所有数据成员具有相同的起始地址。例子如下:
union _GLKVector4
{
struct { float x, y, z, w; };
struct { float r, g, b, a; };
struct { float s, t, p, q; };
float v[4];
} __attribute__((aligned(16)));
typedef union _GLKVector4 GLKVector4;
一个union只分配一个足够大的空间来容纳最大长度的数据成员,以上例子而言,最大长度是struct形态,所以GLKVector4的空间大小就是struct数据类型的大小。
二、大小端模式对union类型的影响
大端模式(Big_endian): 字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
大端模式.png
小端模式(Little_endian): 字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。
小端模式.png
来看下例子:
union
{
int I;
char a[2];
}*p, u;
p =&u;
p->a[0] = 0x39;
p->a[1] = 0x38;
p.i的值应该是多少呢?
分析问题:根据a[0],a[1]对应的地址可以看出 ,低字节存储在高地址中,所以得出大端模式
。
union 型数据所占的空间等于其最大的成员所占的空间。
对union 型的成员的存取都是相对于该联合体基地址的偏移量为0 处开始.
也就是联合体的访问不论对哪个变量的存取都是从union 的首地址位置开始。
以此得出结论:
p.i = 0;
三、如何用程序去判断当前系统的存储模式?
请写一个C 函数,若处理器是Big_endian 的,则返回0;若是Little_endian 的,则返回1。
问题分析:
- 先分析一下,按照上面关于大小端模式的定义,假设int 类型变量i 被初始化为1。
- 变量i 占4 个字节,但只有一个字节的值为1,另外三个字节的值都为0。
- 如果取出低地址上的值为0,毫无疑问,这是大端模式;
- 如果取出低地址上的值为1,毫无疑问,这是小端模式。
- 既然如此,我们完全可以利用union 类型数据的特点:所有成员的起始地址一致。
代码如下
int checkSystem( )
{
union check
{
int I;
char ch;
} c;
c.i = 1;
return (c.ch ==1);
}
四、分析下面的代码
在x64 系统下,输出的值为多少?
#include <stdio.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a[5]={1,2,3,4,5};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)(a+1);
printf("%x,%x\n",ptr1[-1],*ptr2);
}
return 0;
}
- 声明一个int类型的数组, 大小为5。
-
a
即数组a中第一个元素的地址。 -
&a
取得整个数组a的地址。注意:a和&a的地址是一致的。 -
*
求地址存储的值。 -
(int *)(&a + 1)
相当于整个数组偏移的一个数组的长度,即:4(int所占的字节) * 5(5个元素) = 20 bytes, 然后赋值给ptr1指针。 -
(int *)(a+1)
相当于指向数组的第一个元素地址,然后偏移了4(int所占的字节数)个bytes。所以ptr2指向了数组第二个元素的地址。即:*ptr2 = 2。 -
ptr1[-1]
可以演变为*(ptr1 - 1)
,即:ptr1减去4(int的字节)bytes,然后求此时地址对应的值。
附上流程图:
案例分析图.png