c/c++ 中字节序的优化和可移植转换
Optimal and portable conversion of endian in c/c++
给定一个包含我需要解析的 32 位小端字段的二进制文件,我想编写独立于执行该代码的机器的字节顺序正确编译的解析代码。目前我使用
uint32_t fromLittleEndian(const char* data){
return uint32_t(data[3]) << (CHAR_BIT*3) |
uint32_t(data[2]) << (CHAR_BIT*2) |
uint32_t(data[1]) << CHAR_BIT |
data[0];
}
然而,这会生成不理想的程序集。在我的机器上 g++ -O3 -S
产生:
_Z16fromLittleEndianPKc:
.LFB4:
.cfi_startproc
movsbl 3(%rdi), %eax
sall , %eax
movl %eax, %edx
movsbl 2(%rdi), %eax
sall , %eax
orl %edx, %eax
movsbl (%rdi), %edx
orl %edx, %eax
movsbl 1(%rdi), %edx
sall , %edx
orl %edx, %eax
ret
.cfi_endproc
为什么会这样?我如何说服它在小端机器上编译时生成最佳代码:
_Z17fromLittleEndian2PKc:
.LFB5:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
这是我编译得到的:
uint32_t fromLittleEndian2(const char* data){
return *reinterpret_cast<const uint32_t*>(data);
}
因为我知道我的机器是小端的,所以我知道上面的汇编是最优的,但是如果在大端的机器上编译就会失败。它还违反了严格的别名规则,因此如果内联它甚至在小端机器上也可能产生 UB。是否有将被编译为最佳汇编的有效代码if possible?
因为我希望我的函数内联很多,所以任何类型的运行时字节序检测都是不可能的。编写最佳 C/C++ 代码的唯一替代方法是使用编译时字节序检测,如果目标字节序不少,则使用 template
s 或 #define
s 回退到低效代码-endian。然而,这似乎很难移植。
据我所知,各种平台库都是通过#defining 宏来实现这一点的,基于#define BIG_ENDIAN 的值,字节序交换例程。在源字节顺序与目标字节顺序匹配的情况下,您可以:
#ifdef LITTLE_ENDIAN
#define fromLittleEndian(x) (x)
#else
#define fromLittleEndian(x) _actuallySwapLittle((x))
#endif
例如:
简短回答 - 使用 htonl - 它会在 wazoo 中得到优化
给定一个包含我需要解析的 32 位小端字段的二进制文件,我想编写独立于执行该代码的机器的字节顺序正确编译的解析代码。目前我使用
uint32_t fromLittleEndian(const char* data){
return uint32_t(data[3]) << (CHAR_BIT*3) |
uint32_t(data[2]) << (CHAR_BIT*2) |
uint32_t(data[1]) << CHAR_BIT |
data[0];
}
然而,这会生成不理想的程序集。在我的机器上 g++ -O3 -S
产生:
_Z16fromLittleEndianPKc:
.LFB4:
.cfi_startproc
movsbl 3(%rdi), %eax
sall , %eax
movl %eax, %edx
movsbl 2(%rdi), %eax
sall , %eax
orl %edx, %eax
movsbl (%rdi), %edx
orl %edx, %eax
movsbl 1(%rdi), %edx
sall , %edx
orl %edx, %eax
ret
.cfi_endproc
为什么会这样?我如何说服它在小端机器上编译时生成最佳代码:
_Z17fromLittleEndian2PKc:
.LFB5:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
这是我编译得到的:
uint32_t fromLittleEndian2(const char* data){
return *reinterpret_cast<const uint32_t*>(data);
}
因为我知道我的机器是小端的,所以我知道上面的汇编是最优的,但是如果在大端的机器上编译就会失败。它还违反了严格的别名规则,因此如果内联它甚至在小端机器上也可能产生 UB。是否有将被编译为最佳汇编的有效代码if possible?
因为我希望我的函数内联很多,所以任何类型的运行时字节序检测都是不可能的。编写最佳 C/C++ 代码的唯一替代方法是使用编译时字节序检测,如果目标字节序不少,则使用 template
s 或 #define
s 回退到低效代码-endian。然而,这似乎很难移植。
据我所知,各种平台库都是通过#defining 宏来实现这一点的,基于#define BIG_ENDIAN 的值,字节序交换例程。在源字节顺序与目标字节顺序匹配的情况下,您可以:
#ifdef LITTLE_ENDIAN
#define fromLittleEndian(x) (x)
#else
#define fromLittleEndian(x) _actuallySwapLittle((x))
#endif
例如:
简短回答 - 使用 htonl - 它会在 wazoo 中得到优化