【问题标题】:Bitfield struct size different between gcc and MSFT CLgcc和MSFT CL之间的位域结构大小不同
【发布时间】:2017-09-16 04:16:24
【问题描述】:

我有以下代码:

#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\clang 和 Visual C++ (Microsoft CL) 之间得到不同的结果, 当我在 Compiler Explorer 中比较汇编代码时,我得到以下信息:

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

是 Visual C++ 编译器错误还是未定义行为?

【问题讨论】:

  • 几乎所有带有 bitfeilds 的东西都是实现定义的行为。您最有可能看到的是 gcc/clang 很聪明,并将 4 个字段优化为 2 个 uint64_ts,其中 msvs 添加了一个额外的字段,因此您没有跨越两个 uint64_ts 的位文件

标签: c++ c++11 gcc visual-c++ bit-fields


【解决方案1】:

我相信这是未定义的行为。 @NathanOliver 有正确的答案:GCC 和 Clang 跨越了两个 uint64_t 值。但是,阅读它是有代价的:在Compiler Explorer 上查看这个非常相似的代码示例,其中 GCC 现在必须读取两个字段并进行一些数学运算以给出第二个值。

【讨论】:

    【解决方案2】:

    如果你想让两个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;
    };
    

    您也可以在 GCC 中使用 -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()
    

    【讨论】:

    • 谢谢,我不想添加填充,我想在内存上使用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多