簡體   English   中英

重新排序位域神秘地改變了結構的大小

[英]Reordering bit-fields mysteriously changes size of struct

出於某種原因,我有一個結構需要跟蹤 56 位信息,這些信息被排序為 4 包 12 位和 2 包 4 位。 這總共有 7 個字節的信息。

我嘗試了一個像這樣的字段

struct foo {
    uint16_t R : 12;
    uint16_t G : 12;
    uint16_t B : 12;
    uint16_t A : 12;
    uint8_t  X : 4;
    uint8_t  Y : 4;
};

並且驚訝地看到sizeof(foo)在我的機器(linux x86_64 盒子)上使用 g++ 版本 12.1 評估為 10。 我嘗試像這樣重新排序字段

struct foo2 {
    uint8_t  X : 4;
    uint16_t R : 12;
    uint16_t G : 12;
    uint16_t B : 12;
    uint16_t A : 12;
    uint8_t  Y : 4;
};

令我驚訝的是現在的大小為 8 字節,這正是我最初的預期。 它的大小與我期望的第一個解決方案有效產生的結構相同:

struct baseline {
    uint16_t first;
    uint16_t second;
    uint16_t third;
    uint8_t  single;
};

我知道大小和 alignment 和結構打包,但我真的很難理解為什么第一次排序會增加 2 個額外的字節。 沒有理由添加超過 1 個字節的填充,因為我請求的 56 位可以正好包含 7 個字節。

最小的工作示例在 Wandbox 上試用

我錯過了什么?

PS:如果我們將uint8_t更改為uint16_t ,這些都不會改變

如果我們創建一個struct foo的實例,將其清零,設置字段中的所有位,並打印字節,並對每個字段執行此操作,我們會看到以下內容:

R: ff 0f 00 00 00 00 00 00 00 00 
G: 00 00 ff 0f 00 00 00 00 00 00 
B: 00 00 00 00 ff 0f 00 00 00 00 
A: 00 00 00 00 00 00 ff 0f 00 00 
X: 00 00 00 00 00 00 00 f0 00 00 
Y: 00 00 00 00 00 00 00 00 0f 00 

所以似乎正在發生的是每個 12 位字段都從一個新的 16 位存儲單元開始。 然后第一個 4 位字段填充前面 16 位單元中的剩余位,然后最后一個字段占用最后一個單元中的 4 位。 這占用了 9 個位,並且由於最大的字段,在這種情況下是位字段存儲單元,是 2 個字節寬,因此在末尾添加了一個字節的填充。

因此,具有 16 位基本類型的 12 位字段似乎保存在單個 16 位存儲單元中,而不是在多個存儲單元之間拆分。

如果我們對修改后的結構做同樣的事情:

X: 0f 00 00 00 00 00 00 00 
R: f0 ff 00 00 00 00 00 00 
G: 00 00 ff 0f 00 00 00 00 
B: 00 00 00 00 ff 0f 00 00 
A: 00 00 00 00 00 00 ff 0f 
Y: 00 00 00 00 00 00 00 f0 

我們看到X占用了前16位存儲單元的4位,然后R占用了剩下的12位。 字段的rest如前填寫。 這導致使用了 8 個字節,因此不需要額外的填充。

雖然位域排序的確切細節是實現定義的,但C 標准確實設置了一些規則。

從第 6.7.2.1p11 節開始:

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

和 6.7.2.1p15:

在結構 object 中,非位域成員和位域所在的單元的地址按聲明順序遞增。

暫無
暫無

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

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