2017-04-19 36 views
1

我有以下代碼:位域結構尺寸gcc和MSFT CL之間不同

#include <cstdint> 

#pragma pack(1) 
using MyType_t = union { 
    uint8_t buffer[16]; 
    struct { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
}; 
#pragma pack() 

int main() 
{ 
    return sizeof(MyType_t); 
} 

我得到的gcc \鐺和Visual C之間不同的結果++(微軟CL), 當我比較彙編代碼在編譯器Explorer和我得到了以下:

鐺(-std = C++ 11 -O3)

main:         # @main 
     mov  eax, 16 
     ret 

X86-64 GCC 6.3(-O3)

main: 
     mov  eax, 16 
     ret 

x86-64的CL 19 2017 RTW(-Ox)

main PROC 
     mov  eax, 24 
     ret  0 
main ENDP 

是它Visual C++編譯錯誤或它是未定義的行爲?

+3

幾乎所有與bitfeilds是實現定義的行爲。最有可能的是你看到的是gcc/clang很聰明,並且將4個字段優化成2個'uint64_t's,其中msvs增加了一個,所以你沒有一個跨越兩個'uint64_t'的位文件。 – NathanOliver

回答

1

我相信這是未定義的行爲。 @NathanOliver有正確的答案:GCC和Clang跨越了兩個uint64_t值。當你閱讀它時有一定的代價:在Compiler Explorer上看到這個非常相似的代碼示例,其中GCC現在必須讀取兩個字段並進行一些數學運算以提供第二個值。

1

如果你希望兩個layous是兩種編譯器,你可以使用GCC的__attribute__((ms_struct))指令,讓它能夠使用微軟的位域佈局算法之間是一致的:

using MyType_t 
= union { 
    uint8_t buffer[16]; 
    struct __attribute__((ms_struct)) { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
}; 

您還可以使用-mms-bitfields選項與海灣合作委員會,但這是一個可能會破壞其他代碼的ABI更改選項。

如果你想以另一種方式,並迫使微軟的編譯器使用GCC的位域佈局,我不認爲有任何屬性或選項來做到這一點。您必須更改代碼並拆分b成員,使其不會跨越64位邊界。例如:

#pragma pack(1) 
typedef union { 
    uint8_t buffer[16]; 
#ifdef USE_GCC_BITFIELDS 
    struct __attribute__((gcc_struct)) { 
     uint64_t a   : 55; 
     uint64_t b   : 24; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
    uint64_t get_a() { return fields.a; } 
    uint64_t get_b() { return fields.b; } 
    uint64_t get_c() { return fields.c; } 
    uint64_t get_d() { return fields.d; } 
#elif defined(USE_MS_BITFIELDS) 
    struct { 
     uint64_t a   : 55; 
     uint64_t bl  : 9; 
     uint64_t bh  : 15; 
     uint64_t c   : 1;  
     uint64_t d   : 48; 
    }fields; 
    uint64_t get_a() { return fields.a; } 
    uint64_t get_b() { return fields.bl | (fields.bh << 9); } 
    uint64_t get_c() { return fields.c; } 
    uint64_t get_d() { return fields.d; } 
#else /* portable code that should work anywhere */ 
    unsigned long long get_ull(int i) { 
     typedef unsigned long long ull; unsigned char *p = buffer + i; 
     return (ull) p[0] | ((ull) p[1] << 8) | ((ull) p[2] << 16) | ((ull) p[3] << 24) 
      | ((ull) p[4] << 32) | ((ull) p[5] << 40) | (((ull) p[6]) << 48) 
      | ((ull) p[7] << 56); } 
    unsigned long long get_a() { return get_ull(0) & ((1ULL << 55) - 1); } 
    unsigned get_b() { return (buffer[6] >> 7) | (buffer[7] << 1) 
      | (buffer[8] << 9) | ((buffer[9] & 0x7F) << 17); } 
    unsigned get_c() { return buffer[9] >> 7; } 
    unsigned long long get_d() { return get_ull(8) >> 16; } 
#endif 

} MyType_t; 
#pragma pack() 
+0

謝謝,要添加填充,我想在內存中使用它。 – Baget