没有ntohs的字节顺序交换

时间:2012-04-26 21:09:57

标签: c++ endianness

我正在编写一个ELF分析器,但是我在转换字节顺序时遇到了一些麻烦。我有函数来确定分析器的字节顺序和目标文件的endiannness。

基本上,有四种可能的情况:

  1. 大端编译分析器在大端目标文件上运行
    • 无需转换
  2. 一个大端编译分析器在一个小端对象文件上运行
    • 字节顺序需要交换,但是ntohs / l()和htons / l()都是大端机器上的空宏,因此它们不会交换字节顺序。 这是问题
  3. 一个小端编译分析器在大端目标文件上运行
    • 字节顺序需要交换,因此使用htons()交换字节顺序
  4. 一个小端编译分析器在一个小端对象文件上运行。
    • 无需转换
  5. 是否有一个函数可用于显式交换字节顺序/更改字节顺序,因为ntohs / l()和htons / l()会考虑主机的字节序,有时不会转换?或者我是否需要查找/编写自己的交换字节顺序函数?

5 个答案:

答案 0 :(得分:9)

我认为值得提出The Byte Order Fallacy文章,由Rob Pyke(Go的作者之一)。

如果你做得对 - 即你不假设关于你的平台字节顺序的任何事情 - 那么它就会起作用。您需要关心的是ELF格式文件是否在 Little Endian Big Endian 模式下。

来自文章:

  

假设您的数据流具有小端编码的32位整数。以下是如何提取它(假设无符号字节):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
  

如果它是big-endian,这是如何提取它:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

让编译器担心优化它。

答案 1 :(得分:7)

endian.h中的Linux there are several conversion functions中,允许在任意字节序之间进行转换:

uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);

uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);

uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);

编辑,不太可靠的解决方案。您可以使用union以任何顺序访问字节。这很方便:

union {
    short number;
    char bytes[sizeof(number)];
};

答案 2 :(得分:3)

  

我是否需要查找/编写自己的交换字节顺序功能?

是的,你这样做。但是,为了方便起见,我向您推荐这个问题:How do I convert between big-endian and little-endian values in C++?给出了编译器特定字节顺序交换函数的列表,以及字节顺序交换函数的一些实现。

答案 3 :(得分:1)

ntoh函数可以在大端和小端之间进行交换。有些系统也是“中间端”,其中字节被扰乱,而不是按照这种或那种方式排序。

无论如何,如果您关心的只是大端和小端,那么您需要知道的是主机和目标文件的字节顺序是否不同。您将拥有自己的功能,无条件地交换字节顺序,您可以根据是否host_endianess()==objectfile_endianess()来调用它。

答案 4 :(得分:0)

如果我想一个可以在Windows或Linux上运行的跨平台解决方案,我会写一些类似的东西:

#include <algorithm>

// dataSize is the number of bytes to convert.
char le[dataSize];// little-endian
char be[dataSize];// big-endian

// Fill contents in le here...
std::reverse_copy(le, le + dataSize, be);