[英]Bitfield struct size different between gcc and MSFT CL
I have the following code: 我有以下代码:
#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);
}
I'm getting different result between gcc\\clang and Visual C++ (Microsoft CL), when I compare the assembly code in Compiler Explorer and I got the following: 当我在Compiler Explorer中比较汇编代码并得到以下信息时,我在gcc \\ clang和Visual C ++(Microsoft CL)之间得到了不同的结果:
clang (-std=c++11 -O3 ) 铛(-std = c ++ 11 -O3)
main: # @main
mov eax, 16
ret
x86-64 gcc 6.3 (-O3) x86-64 gcc 6.3(-O3)
main:
mov eax, 16
ret
x86-64 CL 19 2017 RTW (-Ox) x86-64 CL 19 2017 RTW(-Ox)
main PROC
mov eax, 24
ret 0
main ENDP
is it Visual C++ compiler bug or it is undefined behavior ? 是Visual C ++编译器错误还是未定义的行为?
I believe this is undefined behavior. 我相信这是未定义的行为。 @NathanOliver has the right answer: GCC and Clang are spanning the two
uint64_t
values. @NathanOliver有正确的答案:GCC和Clang跨越两个
uint64_t
值。 There's a cost when you read it though: see this very similar code sample on the Compiler Explorer where GCC now has to read two fields and do some math to give second value. 但是,在阅读时会付出代价:在Compiler Explorer上查看此非常相似的代码示例,其中GCC现在必须读取两个字段并做一些数学运算以得出第二个值。
If you want the two layous to be consistent between the two compilers you can use GCC's __attribute__((ms_struct))
directive to have it use Microsoft's bitfield layout algorithm: 如果要使两个编译器之间的布局一致,可以使用GCC的
__attribute__((ms_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;
};
You can also use the -mms-bitfields
option with GCC, but this an ABI changing option that could break other code. 您还可以在GCC中使用
-mms-bitfields
选项,但这是一个ABI更改选项,可能会破坏其他代码。
If you want to go the other way, and force Microsoft's compiler to use GCC's bitfield layout, I don't think there's any attribute or option to do that. 如果您想采用另一种方法,并迫使Microsoft的编译器使用GCC的位域布局,我认为没有任何属性或选项可以这样做。 You would have to change your code and split the
b
member so it doesn't cross the 64-bit boundary. 您将不得不更改代码并拆分
b
成员,以使其不会越过64位边界。 Something like: 就像是:
#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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.