簡體   English   中英

使用位域和聯合的意外行為

[英]Unexpected behaviour using bit-fields and unions

我正在試驗位域和聯合並創建了這個:

union REG{
    struct{
        char posX: 7;
        char posY: 7;
        unsigned char dir:  2;
    };

    unsigned short reg;
};

當我運行sizeof( short ) ,我得到 2,但是當我運行sizeof( REG ) ,我得到 4。這對我來說很奇怪,因為當我對這些位求和時得到 7+7+2=16,這就是大小以 2 字節數據類型的位為單位。

我目前正在使用 Dev-C++ 編輯器和編譯器TDM-GCC 9.9.2 64-bit Debug

這是我的第一個問題,如果您需要更多信息,請告訴我...提前致謝!

編輯:經過進一步的實驗,我意識到當我將 posX 和 posY 的大小設置為 6 位時,大小是相同的(2 個字節)。 但這仍然令人困惑,因為總和是 14 位,小於 2 個字節......

編輯 2:感謝 AviBerger,我意識到將 char/unsigned char 數據類型替換為 short/unsigned short '''sizeof( REG )''' 的結果變為 2。但我仍然無法弄清楚“為什么會這樣發生?”

從我們有的規格

一個實現可以分配任何足夠大的可尋址存儲單元來保存一個位域。 如果剩余足夠的空間,緊跟在結構中另一個位域之后的位域應被打包到同一單元的相鄰位中。 如果剩余空間不足,則不適合的位字段是否放入下一個單元或與相鄰單元重疊是實現定義的。 單元內位域的分配順序(高階到低階或低階到高階)是實現定義的。 可尋址存儲單元的對齊方式未指定。

因此,實際行為取決於編譯器為位域選擇的大小分配單元,以及它是否允許字段跨越多個分配單元。 這種選擇是實現定義的,但一個常見的實現是使用位域的聲明類型作為分配單元,並且不允許跨越分配單元邊界。 因此,當您使用 (unisgned) 字符時,它使用 8 位分配單元。 這意味着沒有兩個位域可以組合成一個字節(7+7 > 8 和 7+2 > 8),因此它最終占用 3 個分配單元(字節),然后向上舍入為 4 以進行對齊,當結合短聯。

當您將位域大小更改為 6 時,現在第二個和第三個位域可以容納一個字節 (6+2 = 8),因此它只需要兩個分配單元。

當您將位域類型更改為short它使用 16 位分配單元,因此所有 3 個位域都可以放入一個分配單元。

使用structunion時有幾個技巧點。 最常見的是字段被大量填充以與 CPU 的字長對齊。

struct {
     char   c1;
     char   c2;
} s1;

看起來它應該是一個兩字節的結構,但令人驚訝的是, sizeof (s1)通常不是 2,而是 4,甚至是 8。即使在 1980 年代的 16 位機器上也是如此。

這是因為 C 和 C++ 編譯器會將結構的每個char元素與兩字節或四字節邊界對齊。 我還沒有看到結構元素與 8 字節邊界對齊,但是我們還沒有那么需要 64 位架構。

解決方案是調用一個編譯選項來“打包結構”。 這可以在編譯器命令行上完成,也可以在結構聲明之前包含合適的 #pragma 選項:

#pragma pack(1)   // this particular syntax is valid for many compilers

struct {
     char  c11;
     char  c12;
} s2;

#pragma pack(4)

取自標准(n4835):

11.4.9 位域 [class.bit]
1 [...] 類對象中位域的分配是實現定義的。 位域的對齊是實現定義的。 位域被打包到一些可尋址的分配單元中。 [注意:位域跨越某些機器上的分配單元而不是其他機器。 位域在某些機器上從右到左分配,在其他機器上從左到右分配。 ——尾注]

如您所見,大小和對齊方式是實現定義的。 因此,您可能會在其他編譯器/平台上獲得預期的行為,但在您的編譯器/平台上,您會得到與預期不同的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM