因此,根据此处的C编译器标准:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

我们发现无法精确确定在C编译器中如何实现位字段的要求。显然,只要位域的行为与任何其他标量域一样,任何事情都会发生。文档部分6.7.2.1-10说:


  “一个实现可以分配任何大容量的可寻址存储单元
  足以容纳一个位域。如果有足够的空间,则使用一个位域
  紧随结构中的另一个位字段后,应进行打包
  放入同一单元的相邻位。如果空间不足,
  是否将不合适的位字段放入下一个单元或
  重叠的相邻单元是由实现定义的。的顺序
  一个单元内的位域分配(高阶到低阶或
  从低阶到高阶)由实作定义。的对齐
  未指定可寻址存储单元。”


对于许多声称“您不能信任位域”或“位域不可移植”的人来说,这种对编译器的迫在眉睫的自由似乎是一站式服务。该警报表明,整个编译器编写者和CPU制造商在星空中密谋,只是因为标准允许而笑着急于进行一些奇特的位域调整和对齐。

这些疯狂的波西米亚风格的编译器/ CPU设计人员在哪里致力于确保位域永远不依赖和不可移植,这在哪里提供证据?我想看看火星上绿人的确凿证据。

我已经附上了简单易懂的C ++源代码,以告诉有关使用C ++编译器的任何系统的位域真相。我要问社区,不是征求意见,而是要向您的系统和编译器提供确切的输出证据,以证明它们与发布的结果有所不同。如果与发布的结果相比,我有能力对整个C / C ++社区进行相同/不相同的投票,我想知道百分比是多少?

#include <stdio.h>

/*
 A simple program to illustrate the bitfields actual internal compiled layout.
 Results depend on machine architecture and compiler implementation and flags.
*/

typedef unsigned long long int ulli;

struct bitf
{
    //   field      bits  offset
    ulli f0         : 1; // 0
    ulli f1         : 2; // 1
    ulli f3         : 3; // 3
    ulli f7         : 4; // 6
    ulli f15        : 5; // 10
    ulli f31        : 6; // 15
    ulli f63        : 7; // 21
    ulli f127       : 8; // 28
    ulli f255       : 9; // 36
    ulli f511       :10; // 45
    ulli end        : 9; // 55
                         // 64

    bitf():
         f0         ( 0 )
        ,f1         ( 1 )
        ,f3         ( 3 )
        ,f7         ( 7 )
        ,f15        ( 15 )
        ,f31        ( 31 )
        ,f63        ( 63 )
        ,f127       ( 127 )
        ,f255       ( 255 )
        ,f511       ( 511 )
        ,end        ( 0 )
    {}

    ulli get_shft() const
    {
        ulli bits=0;
        bits <<= 9; bits |=   0;
        bits <<=10; bits |= 511;
        bits <<= 9; bits |= 255;
        bits <<= 8; bits |= 127;
        bits <<= 7; bits |=  63;
        bits <<= 6; bits |=  31;
        bits <<= 5; bits |=  15;
        bits <<= 4; bits |=   7;
        bits <<= 3; bits |=   3;
        bits <<= 2; bits |=   1;
        bits <<= 1; bits |=   0;
        return bits;
    }

    ulli get_cast() const
    {
        ulli bits = *((ulli*)(this));
        return bits;
    }
};

int main()
{
    bitf bf;
    ulli shft = bf.get_shft();
    ulli cast = bf.get_cast();

    printf("sizeof(ulli) is %zu\n\n",sizeof(ulli));
    printf("shft%scast\n\n",(shft==cast)?"==":"!=");
    printf("BITS from MSB 63 (left) down to LSB 0 (right)\n");
    printf("    : "); for(int i=63; i>=0; i--) printf("%c",(i%10)==0 ? i/10 +'0' : ' '); printf("\n");
    printf("    : "); for(int i=63; i>=0; i--) printf("%d",i%10); printf("\n");
    printf("shft: "); for(int i=63; i>=0; i--) printf("%llu",(shft>>i)&1); printf("\n");
    printf("cast: "); for(int i=63; i>=0; i--) printf("%llu",(cast>>i)&1); printf("\n");
    printf("    : ====----====----====----====----====----====----====----====----\n");
    printf("shft: "); for(int i=15;i>=0;i--) printf("%4llx",(shft>>(i*4)&0xf)); printf("\n");
    printf("cast: "); for(int i=15;i>=0;i--) printf("%4llx",(cast>>(i*4)&0xf)); printf("\n");
    printf("    : ====----====----====----====----====----====----====----====----\n");
    unsigned char *pb;
    pb = (unsigned char*)(&shft);
    printf("shft: "); for(int i=sizeof(shft)-1; i>=0; i--) printf("%8x", pb[i]); printf("\n");
    pb = (unsigned char*)(&cast);
    printf("cast: "); for(int i=sizeof(cast)-1; i>=0; i--) printf("%8x", pb[i]); printf("\n");
    printf("\n");

    printf("<ENTER>"); getchar();
    return 0;
}


Intel Core i7,Win10,VS2015、64位版本的结果

sizeof(ulli) is 8

shft==cast

BITS from MSB 63 (left) down to LSB 0 (right)
    :    6         5         4         3         2         1         0
    : 3210987654321098765432109876543210987654321098765432109876543210
shft: 0000000000111111111011111111011111110111111011111011110111011010
cast: 0000000000111111111011111111011111110111111011111011110111011010
    : ====----====----====----====----====----====----====----====----
shft:    0   0   3   f   e   f   f   7   f   7   e   f   b   d   d   a
cast:    0   0   3   f   e   f   f   7   f   7   e   f   b   d   d   a
    : ====----====----====----====----====----====----====----====----
shft:        0      3f      ef      f7      f7      ef      bd      da
cast:        0      3f      ef      f7      f7      ef      bd      da

<ENTER>

最佳答案

位域可以不同的一种常见方式是位字节序。小字节序机器将首先具有低阶位,而大字节序机器将具有高阶位。

例如,以下是struct iphdr的定义,该定义对IP标头进行建模,该标头取自CentOS 7.2系统上的/usr/include/netinet/ip.h:

struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;
    u_int16_t tot_len;
    u_int16_t id;
    u_int16_t frag_off;
    u_int8_t ttl;
    u_int8_t protocol;
    u_int16_t check;
    u_int32_t saddr;
    u_int32_t daddr;
    /*The options start here. */
  };


该结构旨在直接在包含IP数据报头的原始IP数据报的缓冲区上分层。请注意,versionihl字段的顺序取决于字节序。

并参考以下内容:


  一整群的编译器作者和CPU制造商密谋在星空中,只是因为标准允许而笑着急于做一些奇特的位域调整和对齐。


编译器作者的确可以迅速利用标准未定义或未指定的任何行为来执行各种优化,这些优化可能会使那些认为C始终充当汇编语言的瘦包装的人感到惊讶。

关于c++ - C/C++在实际实践中,位域字节序确实是一个问题吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57081289/

10-13 06:18