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:
clang (-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
is it Visual C++ compiler bug or it is undefined behavior ?
I believe this is undefined behavior. @NathanOliver has the right answer: GCC and Clang are spanning the two uint64_t
values. 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.
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:
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.
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. You would have to change your code and split the b
member so it doesn't cross the 64-bit boundary. 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()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.