2012-07-01 46 views
9

在C11,下面的結構工作的一個錯誤:在GCC實現位字段

struct S { 
    unsigned a : 4; 
    _Bool b : 1; 
}; 

獲取由GCC奠定了由此使用4位的unsigned(4字節),接着是_Bool (4字節),其中使用1位,總大小爲8字節。

請注意,C99和C11明確允許_Bool作爲位域成員。在C11標準(也可能是C99太)也正在§6.7.2.1「結構和聯合說明」¶11指出:

實現可分配任何可尋址存儲單元,大到足以容納一個位字段。如果剩餘足夠的空間,緊接在結構中的另一位字段之後的位字段應被打包到相同單元的相鄰位中。

因此,我相信構件上方b應已包裝成分配用於所述構件a存儲單元中,從而導致總的大小4個字節的結構。

GCC正確的行爲,並使用相同類型的兩個成員時,確實發生了包裝,或者當一個unsigned和其他signed,但類型unsigned_Bool似乎被認爲是由GCC它來處理它們太明顯正確。

有人可以證實我對標準的解釋,這確實是一個GCC錯誤?

我也對解決方法感興趣(一些編譯器開關,編譯指示,__attribute__ ...)。

我使用gcc 4.7.0與-std=c11(儘管其它設置顯示相同的行爲。)

+0

請注意,GCC擴展'__attribute__((packed))'可以應用於這裏的成員,但與此問題正交(它導致大小爲4 + 1 = 5的結構,即具有相同的問題。) – ndkrempel

+0

相關:http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bools(但是指的是C++,它在位域上的表述並不盡如人意)。 – ndkrempel

+0

根據上面鏈接問題的答案,這種行爲在gcc 4.2.4中沒有發生,所以可能是從那時起迴歸。 – ndkrempel

回答

10

所描述的行爲是與C99和C11的標準不兼容,但提供了一種用於二進制兼容性與MSVC編譯器(它具有不尋常的結構堆積行爲)。

幸運的是,它可以在應用於該結構的代碼__attribute__((gcc_struct))或命令行開關-mno-ms-bitfields(請參閱documentation)中禁用。

+0

這是記錄在任何地方嗎?我似乎無法找到任何有用的'mno-ms-bitfields' –

+0

https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#x86-Options – ndkrempel

+0

謝謝,不知道爲什麼我可以之前沒有找到。因爲評論不是長期的,所以將它編輯到你的答案中會很有用。 –

0

同時使用GCC 4.7.1(自制)和Mac OS X 10.7.4 GCC 4.2.1(LLVM /鐺†)與64位編譯,這個代碼可以產生4-std=c99模式:

#include <stdio.h> 

int main(void) 
{ 
    struct S 
    { 
     unsigned a : 4; 
     _Bool b : 1; 
    }; 
    printf("%zu\n", sizeof(struct S)); 
    return 0; 
} 

這是您在Windows上報告的大小的一半。對我來說這似乎令人驚訝地大(我預計它會是1字節的大小),但平臺的規則是他們是什麼。基本上,編譯器沒有義務遵循你想要的規則;它可以遵循它運行的平臺的規則,以及它有機會的地方,甚至可以定義它運行的平臺的規則。

這下面的程序有輕度可疑行爲(因爲它訪問u.iu.s上次寫入),但顯示該字段a存儲在4個最低顯著位,場b存儲在下一位:

#include <stdio.h> 

int main(void) 
{ 
    union 
    { 
     struct S 
     { 
      unsigned a : 4; 
      _Bool b : 1; 
     } s; 
     int i; 
    } u; 
    u.i = 0; 
    u.s.a = 5; 
    u.s.b = 1; 
    printf("%zu\n", sizeof(struct S)); 
    printf("%zu\n", sizeof(u)); 
    printf("0x%08X\n", u.i); 
    u.s.a = 0xC; 
    u.s.b = 1; 
    printf("0x%08X\n", u.i); 
    return 0; 
} 

輸出:

4 
4 
0x00000015 
0x0000001C 

†i686的-蘋果darwin11-LLV mcc-4.2(GCC)4.2.1(基於Apple Inc. build 5658)(LLVM build 2336.9.00)