2017-04-19 37 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