iOS联合体、位域

从 isa 底层结构引入联合体、位域

在isa底层结构分析中我们简单的介绍过 isa 的底层数据结构

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

通过上述源码发现 isa_t 是一个 union(共用体/联合体),联合体意味着公用内存 , 也就是说 isa 其实总共还是占用 8 个字节内存 , 共 64 个二进制位 。
其中 ISA_BITFIELD(位域) 宏定义在不同架构下表示如下 :

# if __arm64__
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
# elif __x86_64__
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
  • 由于联合体的特性 , cls , bits 以及 struct 都是 8 字节内存 , 也就是说他们在内存中是完全重叠的。
  • 实际上在 runtime中,任何对 struct 的操作和获取某些值,如 extra_rc ,实际上都是通过对 bits 做位运算实现的。
  • bitsstruct 的关系可以看做 : bits 向外提供了操作 struct 的接口,而 struct 本身则说明了 bits 中各个二进制位的定义。

接下来我们深入研究一下联合体、位域。

联合体

  • 结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
  • 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
// 联合体
union {
    char bits;
    // 位域
    struct { // 0000 1111
        char front  : 1;
        char back   : 1;
        char left   : 1;
        char right  : 1;
    };
} _direction;
  1. 联合体中可以定义多个成员,联合体的大小由最大的成员大小决定
  2. 联合体的成员公用一个内存,一次只能使用一个成员
  3. 对某一个成员赋值,会覆盖其他成员的值
  4. 存储效率更高,可读性更强,可以提高代码的可读性,可以使用位运算提高数据的存储效率

位域

结构体中除了可以定义基本数据类型外,还可以使用位域来构建数据成员,也就是说某个数据成员可能只占用结构体中某几个bit位的存储空间。结构体中定义位域的目的主要是为了节省内存空间。假如某个结构体中有 8BOOL 类型的数据成员用来描述 8 种状态。那么我们需要定义 8BOOL 类型的数据成员,这样这个结构体实例就占用了 8 个字节的内存空间,而如果我们使用 位域 来定义的话则可以用一个字节的内存空间就可以表述出来。定义 位域 的格式如下:

struct  {
    // 位域名 : 位域长
    char front : 1;
    char back : 1; 
    char left : 1; 
    char right  : 1;
};
  1. 位结构中的成员可以定义为 unsigned , 也可定义为 signed 或者是 char, 但当成员长度为 1 时, 会被认为是 unsigned 类型。因为单个位不可能具有符号。
  2. 位结构中的成员不能使用数组和指针, 但位结构变量可以是数组和指针, 如果是指针, 其成员访问方式同结构指针。
  3. 位结构总长度(位数), 是各个位成员定义的位数之和(如果类型相同的话)
  4. 位结构成员可以与其它结构成员一起使用。
    • 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过 8 位二进位。
    • 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。
    • 如果相邻位域字段的类型相同,且其位宽之和小于类型的 sizeof 大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止。
#include 

struct test {
    char a : 2;
    char b : 3;
    char c : 1;
};

struct test1 {
    char a : 2;
    char b : 3;
    char c : 7;
};

struct {
    short int a:2;
    int b:4;
    char c;
}test2;

struct {
    int a:2;
    int b:4;
    char c;
}test3;

struct {
    short s1:3;
    short s2:3;
    short s3:3;
}test4;

struct{
    char c1:3;
    char c2:2;
    char c3:2;
}test5;

struct test6
{
    int a:4;
    int b:3;
    char c;
};

int main(int argc, const char * argv[]) {
    // insert code here...
    
    printf("test长度:%d\ntest1长度:%d\ntest2长度:%d\ntest3长度:%d\ntest4长度:%d\ntest5长度:%d\ntest6长度:%d\n",sizeof(struct test),sizeof(struct test1),sizeof(test2),sizeof(test3),sizeof(test4),sizeof(test5),sizeof(struct test6));

    return 0;
}

控制台输出:
test长度:1
test1长度:2
test2长度:4
test3长度:4
test4长度:2
test5长度:1
test6长度:4
Program ended with exit code: 0

小测验:

32位环境下,给定结构体

Struct A
{
    Char t:4;

    Char k:4;

    Unsigned short i:8;

    Unsigned long m;
};

sizeof ( A ) =_____;

A. 6
B. 7
C. 8
D. 上述答案都不对

变量后面加 : 然后加数字表示位域,也就是说着代表按位来存放的,不是按字节,这是计算机为了节约空间的一种方式。char是一个字节(8个位),所以 t和k 加起来刚好8个位,也就是一个字节。然后short 一共16个位放了8个,剩下8个不够后面long存放,所以算两个字节。因为long在32是4个字节,所以一共 1 +2 +4 = 7 。然后进行结构体对齐,所以就是8.

你可能感兴趣的:(iOS联合体、位域)