如何在c++中转换大端值和小端值?

为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。


注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。


当前回答

我只是想在这里添加我自己的解,因为我在任何地方都没有看到它。它是一个小而可移植的c++模板函数,并且只使用比特操作。

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}

其他回答

从大端序到小端序的过程与从小端序到大端序的过程是一样的。

下面是一些示例代码:

void swapByteOrder(unsigned short& us)
{
    us = (us >> 8) |
         (us << 8);
}

void swapByteOrder(unsigned int& ui)
{
    ui = (ui >> 24) |
         ((ui<<8) & 0x00FF0000) |
         ((ui>>8) & 0x0000FF00) |
         (ui << 24);
}

void swapByteOrder(unsigned long long& ull)
{
    ull = (ull >> 56) |
          ((ull<<40) & 0x00FF000000000000) |
          ((ull<<24) & 0x0000FF0000000000) |
          ((ull<<8) & 0x000000FF00000000) |
          ((ull>>8) & 0x00000000FF000000) |
          ((ull>>24) & 0x0000000000FF0000) |
          ((ull>>40) & 0x000000000000FF00) |
          (ull << 56);
}

来这里寻找一个Boost解决方案,失望地离开,但最终在其他地方找到了它。你可以使用boost::endian::endian_reverse。它被模板化/重载了所有的基元类型:

#include <iostream>
#include <iomanip>
#include "boost/endian/conversion.hpp"

int main()
{
  uint32_t word = 0x01;
  std::cout << std::hex << std::setfill('0') << std::setw(8) << word << std::endl;
  // outputs 00000001;

  uint32_t word2 = boost::endian::endian_reverse(word);
  // there's also a `void ::endian_reverse_inplace(...) function
  // that reverses the value passed to it in place and returns nothing

  std::cout << std::hex << std::setfill('0') << std::setw(8) << word2 << std::endl;
  // outputs 01000000

  return 0;
}

示范

虽然,看起来c++23最终用std::byteswap解决了这个问题。(我使用的是c++17,所以这不是一个选项。)

和在C中一样:

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

您还可以声明一个无符号字符的向量,将输入值memcpy放入其中,将字节反向转换为另一个向量,然后将字节memcpy取出,但这将花费比旋转位长几个数量级的时间,特别是对于64位值。

我们已经用模板做到了这一点。你可以这样做:

// Specialization for 2-byte types.
template<>
inline void endian_byte_swapper< 2 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    ushort* p_dest = reinterpret_cast< ushort* >(dest);
    ushort const* const p_src = reinterpret_cast< ushort const* >(src);
    *p_dest = (*p_src >> 8) | (*p_src << 8);
}

// Specialization for 4-byte types.
template<>
inline void endian_byte_swapper< 4 >(char* dest, char const* src)
{
    // Use bit manipulations instead of accessing individual bytes from memory, much faster.
    uint* p_dest = reinterpret_cast< uint* >(dest);
    uint const* const p_src = reinterpret_cast< uint const* >(src);
    *p_dest = (*p_src >> 24) | ((*p_src & 0x00ff0000) >> 8) | ((*p_src & 0x0000ff00) << 8) | (*p_src << 24);
}

有一个叫做BSWAP的汇编指令可以帮你做交换,非常快。 你可以在这里阅读。

Visual Studio,或者更准确地说是Visual c++运行时库,为此提供了平台intrinsic,称为_byteswap_ushort()、_byteswap_ulong()和_byteswap_int64()。其他平台应该也有类似的情况,但我不知道它们会被称为什么。