C++ gcc和MSFT CL之间的位字段结构大小不同
我有以下代码:C++ gcc和MSFT CL之间的位字段结构大小不同,c++,c++11,gcc,visual-c++,bit-fields,C++,C++11,Gcc,Visual C++,Bit Fields,我有以下代码: #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
#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);
}
x86-64 gcc 6.3(-O3)
main: # @main
mov eax, 16
ret
x86-64 CL 19 2017 RTW(-Ox)
main: # @main
mov eax, 16
ret
是VisualC++编译器bug还是未定义行为?< /P> < P>我相信这是未定义的行为。NathanOliver给出了正确的答案:GCC和Clang跨越了两个
uint64\t
值。但是,阅读时会有一个代价:在where上看到这个非常相似的代码示例,GCC现在必须读取两个字段并进行一些数学运算以给出第二个值。如果您希望两个编译器之间的两个布局保持一致,可以使用GCC的\uu属性(ms\u struct))
指令,使其使用Microsoft的位域布局算法:
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;
};
您也可以在GCC中使用-mms位字段
选项,但这是一个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()
#pragma包(1)
typedef联合{
uint8_t缓冲器[16];
#ifdef使用\u GCC\u位字段
结构属性((gcc结构)){
uint64_t a:55;
uint64_t b:24;
uint64_t c:1;
uint64_t d:48;
}田地;
uint64\u t get_a(){返回字段.a;}
uint64_t get_b(){返回字段.b;}
uint64_t get_c(){return fields.c;}
uint64\u t get_d(){return fields.d;}
#定义的elif(使用\u MS\u位字段)
结构{
uint64_t a:55;
uint64\u t bl:9;
uint64_t bh:15;
uint64_t c:1;
uint64_t d:48;
}田地;
uint64\u t get_a(){返回字段.a;}
uint64_t get_b(){return fields.bl|(fields.bh几乎所有使用bitfeilds的内容都是实现定义的行为。您最可能看到的是gcc/clang非常聪明,将4个字段优化为2个uint64_t
s,其中MSV添加了一个额外的字段,这样您就不会有跨越两个uint64_t
s的位字段。hanks,我不想添加填充,我想使用I不依赖记忆。
#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()