用于将字节序独立转换为大字节序的宏

nie*_*sen 5 c endianness c11

是否可以编写一个C包含an uint32_t并将其转换为大端序表示的宏,而不管目标系统是小端还是大端序,以便可以在编译时为常量求值?

我发现了一个问题:字节序转换,无论字节序如何,但是答案仅提供了功能。我的情况是我希望编译时表达式能够编写如下内容:

const uint32_t magic_number = BIGENDIAN32(0x12345678);
Run Code Online (Sandbox Code Playgroud)

Lun*_*din 4

您可以使用union依赖于字节顺序的 a 以及不依赖于字节顺序的位移位。运行时版本:

uint32_t big_endian (uint32_t n)
{
  union
  {
    uint32_t u32;
    uint8_t  u8 [4];
  } be;

  for(size_t i=0; i<4; i++)
  {
    size_t shift = (4-1-i) * 8;
    be.u8[i] = (n >> shift) & 0xFFu;
  }
  return be.u32;
}
Run Code Online (Sandbox Code Playgroud)

u8[0]在大端机器上将始终包含 MS 字节。然而,n >> shift将可移植地抓取相关字节。值得注意的是,当在大端机器上运行时,整个函数只是开销膨胀。

将其转换为丑陋的编译时宏将如下所示:

typedef union
{
  uint32_t u32;
  uint8_t  u8 [4];
} be_t;


#define BIG_ENDIAN(n) ( _Generic((n), uint32_t: (void)0), \
  (be_t){ .u8 = { ((n) >> 24)&0xFFu,                      \
                  ((n) >> 16)&0xFFu,                      \
                  ((n) >>  8)&0xFFu,                      \
                   (n)&0xFFu } }.u32)
Run Code Online (Sandbox Code Playgroud)

check _Generic+,运算符只是为了类型安全,如果使用非标准 C,可以将其删除。该宏使用复合文字形式的临时联合(外部 {}),初始化数组u8(内部 {}),然后返回一个uint32_t值。

尝试BIG_ENDIAN(0x12345678)使用小端 x86 并反汇编,我得到:

mov     esi, 2018915346
Run Code Online (Sandbox Code Playgroud)

2018915346 十二月 = 0x78563412