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 <stdio.h>

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.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容