union 关键字的用法

一、定义:

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

附上demo地址:点击下载

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容