如何将值从主机字节顺序转换为小端?

我需要将一个短值从主机字节顺序转换为小端。 如果目标是大端,我可以使用htons()函数,但唉 – 它不是。

我想我能做到:

swap(htons(val)) 

但是这可能会导致字节被交换两次,从而使结果正确但给我一个性能损失,这在我的情况下是不正确的。

类似于以下内容:

 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 

如果你有一个正确配置的系统(这样预处理器知道目标id是小端还是大端)你就得到了’优化’版本的htoles() 。 否则,您将获得依赖于htons()的潜在非优化版本。 无论如何,你会得到一些有用的东西。

没有什么太棘手,或多或少的便携性。

当然,您可以根据需要使用inline或宏来进一步改进优化的可能性。

您可能希望查看类似“便携式开源线束(POSH)”的内容,以获取定义各种编译器的字节顺序的实际实现。 注意,访问库需要通过伪认证页面(尽管您不需要注册以提供任何个人详细信息): http : //hookatooka.com/poshlib/

这是一篇关于字节序以及如何从IBM确定它的文章:

在C中编写与字节顺序无关的代码:不要让字节序“字节”给你

它包含一个如何在运行时确定字节顺序的示例(您只需要执行一次)

 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", uc[0], uc[1], uc[2], uc[3]); } else { printf(“%X.%X.%X.%X\n", uc[3], uc[2], uc[1], uc[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; } 

然后你应该知道你的字节顺序并有条件地调用htons()。 实际上,甚至不是htons,而只是有条件地交换字节。 编译时,当然。

这个技巧应该是:在启动时,使用具有虚拟值的ntohs ,然后将结果值与原始值进行比较。 如果两个值都相同,那么机器使用大端,否则它是小端。

然后,使用ToLittleEndian方法,该方法不执行任何操作或调用ntohs ,具体取决于初始测试的结果。

(使用评论中提供的信息编辑)

我的经验法则猜测取决于你是否只是一点一点的大端数据,或者只是一个值:

如果只有一个值,那么函数调用开销可能会淹没不必要的字节交换的开销,即使编译器没有优化掉不必要的字节交换。 然后你可能会将值写为套接字连接的端口号,并尝试打开或绑定套接字,这需要与任何类型的位操作相比较的年龄。 所以不要担心。

如果是一个大块,那么你可能会担心编译器不会处理它。 所以做这样的事情:

 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_ENDIANBIG_ENDIAN的正确定义的unit testing可能如下所示:

 #include  #include  #include  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(); }