我需要将主机字节序中的短整型值转换为小端字节序。如果目标是大端字节序,我可以使用htons()函数,但不幸的是 - 它不是。
我想我可以这样做:
swap(htons(val))
但这可能会导致字节被交换两次,虽然结果正确,但会给我带来性能损失,在我的情况下这是不允许的。
我需要将主机字节序中的短整型值转换为小端字节序。如果目标是大端字节序,我可以使用htons()函数,但不幸的是 - 它不是。
我想我可以这样做:
swap(htons(val))
但这可能会导致字节被交换两次,虽然结果正确,但会给我带来性能损失,在我的情况下这是不允许的。
这是一篇关于字节序(endianness)以及如何从IBM确定它的文章:
其中包括一个在运行时确定字节序的示例(您只需要执行一次)。
const int i = 1;
#define is_bigendian() ( (*(char*)&i) == 0 )
int main(void) {
int val;
char *ptr;
ptr = (char*) &val;
val = 0x12345678;
if (is_bigendian()) {
printf(“%X.%X.%X.%X\n", u.c[0], u.c[1], u.c[2], u.c[3]);
} else {
printf(“%X.%X.%X.%X\n", u.c[3], u.c[2], u.c[1], u.c[0]);
}
exit(0);
}
该页面还有一个关于反转字节顺序的方法部分:
short reverseShort (short s) {
unsigned char c1, c2;
if (is_bigendian()) {
return s;
} else {
c1 = s & 255;
c2 = (s >> 8) & 255;
return (c1 << 8) + c2;
}
}
;
short reverseShort (char *c) {
short s;
char *p = (char *)&s;
if (is_bigendian()) {
p[0] = c[0];
p[1] = c[1];
} else {
p[0] = c[1];
p[1] = c[0];
}
return s;
}
#define is_bigendian() ( (*(char*)&i) == 0 )
,我猜测CPU上的分支预测器可能会消除它,从而使得当系统已经是小端字节序时,这实际上成为一个无操作。 - Robert S. Barnes类似以下内容:
unsigned short swaps( unsigned short val)
{
return ((val & 0xff) << 8) | ((val & 0xff00) >> 8);
}
/* host to little endian */
#define PLATFORM_IS_BIG_ENDIAN 1
#if PLATFORM_IS_LITTLE_ENDIAN
unsigned short htoles( unsigned short val)
{
/* no-op on a little endian platform */
return val;
}
#elif PLATFORM_IS_BIG_ENDIAN
unsigned short htoles( unsigned short val)
{
/* need to swap bytes on a big endian platform */
return swaps( val);
}
#else
unsigned short htoles( unsigned short val)
{
/* the platform hasn't been properly configured for the */
/* preprocessor to know if it's little or big endian */
/* use potentially less-performant, but always works option */
return swaps( htons(val));
}
#endif
htoles()
版本。否则,您将得到取决于htons()
的潜在非优化版本。无论如何,您都会得到能够工作的东西。inline
或适当的宏来进一步改善优化可能性。我的经验法则是,这取决于你是否一次性地对大块数据进行小端字节序转换,还是只转换一个值:
如果只转换一个值,那么函数调用开销可能会压倒不必要的字节交换开销,即使编译器没有优化掉不必要的字节交换。然后,您可能会将该值写为套接字连接的端口号,并尝试打开或绑定套接字,这与任何类型的位操作相比都需要更长的时间。所以不要担心它。
如果是大块数据,则您可能会担心编译器无法处理。因此,请执行以下操作:
if (!is_little_endian()) {
for (int i = 0; i < size; ++i) {
vals[i] = swap_short(vals[i]);
}
}
或者在您的架构上查看SIMD指令,这可以使其运行速度更快。
使用任何技巧编写is_little_endian()
。我认为Robert S. Barnes提供的方法是可靠的,但由于通常对于给定的目标,您知道它是大端还是小端,因此您应该有一个特定于平台的头文件,将其定义为评估为1或0的宏。
一如既往,如果您真的关心性能,请查看生成的汇编代码,以查看是否已删除无意义的代码,并将各种替代方案相互比较以确定哪个实际上运行最快。
很遗憾,使用标准C编译时没有真正的跨平台方法来确定系统的字节顺序。我建议在您的config.h
(或任何其他用于构建配置的内容)中添加#define
。
检查LITTLE_ENDIAN
或BIG_ENDIAN
定义是否正确的单元测试可能如下所示:
#include <assert.h>
#include <limits.h>
#include <stdint.h>
void check_bits_per_byte(void)
{ assert(CHAR_BIT == 8); }
void check_sizeof_uint32(void)
{ assert(sizeof (uint32_t) == 4); }
void check_byte_order(void)
{
static const union { unsigned char bytes[4]; uint32_t value; } byte_order =
{ { 1, 2, 3, 4 } };
static const uint32_t little_endian = 0x04030201ul;
static const uint32_t big_endian = 0x01020304ul;
#ifdef LITTLE_ENDIAN
assert(byte_order.value == little_endian);
#endif
#ifdef BIG_ENDIAN
assert(byte_order.value == big_endian);
#endif
#if !defined LITTLE_ENDIAN && !defined BIG_ENDIAN
assert(!"byte order unknown or unsupported");
#endif
}
int main(void)
{
check_bits_per_byte();
check_sizeof_uint32();
check_byte_order();
}
ntohs
,然后将结果值与原始值进行比较。如果两个值相同,则机器使用大端字节序,否则它是小端字节序。
然后,使用一个ToLittleEndian
方法,根据初始测试的结果不执行任何操作或调用ntohs
。
(根据评论中提供的信息进行编辑)