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

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


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


当前回答

如果你正在使用Visual c++,请执行以下操作:包含intrin.h并调用以下函数:

对于16位数字:

unsigned short _byteswap_ushort(unsigned short value);

对于32位数字:

unsigned long _byteswap_ulong(unsigned long value);

对于64位数字:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8位数字(字符)不需要转换。

此外,这些仅定义为无符号值,它们也适用于有符号整数。

对于浮点数和双精度数,要比普通整数困难得多,因为它们可能在主机的字节顺序中。你可以在大端机器上得到小端浮点数,反之亦然。

其他编译器也有类似的特性。

例如,在GCC中,你可以直接调用一些内置程序,如下所示:

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(不需要包含任何东西)。Afaik bits.h也以非gcc为中心的方式声明了相同的函数。

16位交换就是位旋转。

顺便说一句,调用这些内在函数而不是调用自己的内在函数可以获得最好的性能和代码密度。

其他回答

和在C中一样:

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

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

如果你正在使用Visual c++,请执行以下操作:包含intrin.h并调用以下函数:

对于16位数字:

unsigned short _byteswap_ushort(unsigned short value);

对于32位数字:

unsigned long _byteswap_ulong(unsigned long value);

对于64位数字:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8位数字(字符)不需要转换。

此外,这些仅定义为无符号值,它们也适用于有符号整数。

对于浮点数和双精度数,要比普通整数困难得多,因为它们可能在主机的字节顺序中。你可以在大端机器上得到小端浮点数,反之亦然。

其他编译器也有类似的特性。

例如,在GCC中,你可以直接调用一些内置程序,如下所示:

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(不需要包含任何东西)。Afaik bits.h也以非gcc为中心的方式声明了相同的函数。

16位交换就是位旋转。

顺便说一句,调用这些内在函数而不是调用自己的内在函数可以获得最好的性能和代码密度。

这里有一个基本的函数来交换大小端序。它是基本的,但不需要补充库。

void endianness_swap(uint32_t& val) {
    uint8_t a, b, c;
    a = (val & 0xFF000000) >> 24;
    b = (val & 0x00FF0000) >> 16;
    c = (val & 0x0000FF00) >> 8;
    val=(val & 0x000000FF) << 24;
    val = val + (c << 16) + (b << 8) + (a);
}

来这里寻找一个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,所以这不是一个选项。)

在大多数POSIX系统中(虽然不是在POSIX标准中)有end .h,它可以用来确定系统使用的编码。然后是这样的:

unsigned int change_endian(unsigned int x)
{
    unsigned char *ptr = (unsigned char *)&x;
    return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}

这将交换顺序(从大端序到小端序):

如果你有数字0xDEADBEEF(在一个小端序系统中存储为0xEFBEADDE), ptr[0]将是0xEF, ptr[1]是0xBE,等等。

但是如果你想将它用于网络,那么htons, htonl和htonll(以及它们的逆ntohs, ntohl和ntohll)将有助于从主机顺序转换到网络顺序。