与端序无关的转换为大端序的宏
Macro for endian-independent conversion to big endian
是否可以在 C
中编写一个宏,它采用 uint32_t
并将其转换为大端表示,无论目标系统是小端还是大端,这样宏就可以在编译时计算常量?
我发现了这个问题:endianness conversion, regardless of endianness,但是,答案只提供功能。我的情况是我希望编译时表达式能够编写如下内容:
const uint32_t magic_number = BIGENDIAN32(0x12345678);
您可以使用依赖字节序的 union
,以及不依赖字节序的位移位。 运行-时间版本:
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;
}
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)
_Generic
check + ,
运算符仅用于类型安全,如果卡在非标准 C 中,可以将其删除。宏使用复合文字形式的临时联合 (外部 {}),初始化 u8
数组(内部 {}),然后 returns 一个 uint32_t
值。
在 little endian x86 上尝试 BIG_ENDIAN(0x12345678)
并反汇编,我得到:
mov esi, 2018915346
2018915346 dec = 0x78563412
如果您使用的是 GCC,您可以执行以下操作:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define BIGENDIAN32(_a_) __builtin_bswap32 (_a_)
#else
#define BIGENDIAN32(_a_) (_a_)
#endif
注意:没有考虑 PDP 字节序,但你已经明白了
如果您希望代码可移植到其他编译器,您必须替换以下行:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
通过 this thread 的聪明贡献者建议的宏之一,并为字节反转内置定义一个宏。
考虑复合文字 union
.
#define BIGENDIAN32(x) (((union { uint8_t u8[4]; uint32_t u32; }) \
{ {((uint32_t)(x)>>24)&255, ((uint32_t)(x)>>16)&255, \
((uint32_t)(x)>> 8)&255, (uint32_t)(x)&255} }).u32) \
// MSbyte first LSByte last
int main(void) {
const uint32_t magic_number = BIGENDIAN32(0x12345678u);
return (int) magic_number;
}
是否可以在 C
中编写一个宏,它采用 uint32_t
并将其转换为大端表示,无论目标系统是小端还是大端,这样宏就可以在编译时计算常量?
我发现了这个问题:endianness conversion, regardless of endianness,但是,答案只提供功能。我的情况是我希望编译时表达式能够编写如下内容:
const uint32_t magic_number = BIGENDIAN32(0x12345678);
您可以使用依赖字节序的 union
,以及不依赖字节序的位移位。 运行-时间版本:
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;
}
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)
_Generic
check + ,
运算符仅用于类型安全,如果卡在非标准 C 中,可以将其删除。宏使用复合文字形式的临时联合 (外部 {}),初始化 u8
数组(内部 {}),然后 returns 一个 uint32_t
值。
在 little endian x86 上尝试 BIG_ENDIAN(0x12345678)
并反汇编,我得到:
mov esi, 2018915346
2018915346 dec = 0x78563412
如果您使用的是 GCC,您可以执行以下操作:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define BIGENDIAN32(_a_) __builtin_bswap32 (_a_)
#else
#define BIGENDIAN32(_a_) (_a_)
#endif
注意:没有考虑 PDP 字节序,但你已经明白了
如果您希望代码可移植到其他编译器,您必须替换以下行:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
通过 this thread 的聪明贡献者建议的宏之一,并为字节反转内置定义一个宏。
考虑复合文字 union
.
#define BIGENDIAN32(x) (((union { uint8_t u8[4]; uint32_t u32; }) \
{ {((uint32_t)(x)>>24)&255, ((uint32_t)(x)>>16)&255, \
((uint32_t)(x)>> 8)&255, (uint32_t)(x)&255} }).u32) \
// MSbyte first LSByte last
int main(void) {
const uint32_t magic_number = BIGENDIAN32(0x12345678u);
return (int) magic_number;
}