如何以可移植的方式将integer转换为little-endian

How to convert integer to little-endian in a portable way

本文关键字:integer 转换 little-endian 方式 可移植      更新时间:2023-10-16

我想知道以可移植的方式将integer转换为little-endian的推荐方法是什么。

有图书馆吗?

我们可以使用htonl和ntohl,然后进行另一个big-endian到little-endian的转换,但这并不有效。

可移植的方法是将位偏移和掩码用于适当大小的字符串。请注意,我说的是字符串,因为这实际上是您唯一需要关注endianness的时间——在系统之间传输字节时。

如果您想避免不必要的转换(例如,在little-endian体系结构上转换为little-eddian),那么在编译时没有完全可移植的方法。但是,您可以在运行时进行检查以动态选择转换函数集。

这确实有代码无法内联的缺点。以可移植的方式编写转换并使用模板或内联可能更有效。结合半可移植的编译时检查,这将是您所能得到的最好结果。

进一步阅读:在编译时检测Endianness。

这是一个很好的问题。它促使我查看是否有任何方法可以在编译时使用constexpr表达式来确定endianness。

事实证明,如果没有预处理器技巧,这是不可能的,因为在constexpr上下文中进行求值时,无法将整数转换为字节序列(通过强制转换或并集)。

然而事实证明,在gcc中,使用-O2编译时,一个简单的运行时检查会得到优化,因此这实际上是最有效的:

#include <cstdint>
#include <iostream>
constexpr bool is_little_endian()
{
    union endian_tester {
        std::uint16_t   n;
        std::uint8_t    p[4];
    };
    constexpr endian_tester sample = {0x0102}; 
    return sample.p[0] == 0x2;
}
template<class Int>
Int to_little_endian(Int in)
{
  if (is_little_endian()) {
    return in;
  }
  else {
    Int out = 0;
    std::uint8_t* p = reinterpret_cast<std::uint8_t*>(std::addressof(out));
    for (std::size_t byte = 0 ; byte < sizeof(in) ; ++byte)
    {
      auto part = (in >> (byte * 8)) & 0xff;
      *p++ = std::uint8_t(part);
    }
    return out;
  }
}
int main()
{
    auto x = to_little_endian(10); 
    std::cout << x << std::endl;
}

以下是在intel(little-endian)平台上编译时的汇编程序输出:

main:
        subq    $8, %rsp
#
# here is the call to to_little_endian()
#
        movl    $10, %esi
#
# that was it - it's been completely optimised away
#
        movl    std::cout, %edi
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        movq    %rax, %rdi
        call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
        xorl    %eax, %eax
        addq    $8, %rsp
        ret