如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
void writeLittleEndianToBigEndian(void* ptrLittleEndian, void* ptrBigEndian , size_t bufLen )
{
char *pchLittleEndian = (char*)ptrLittleEndian;
char *pchBigEndian = (char*)ptrBigEndian;
for ( size_t i = 0 ; i < bufLen ; i++ )
pchBigEndian[bufLen-1-i] = pchLittleEndian[i];
}
std::uint32_t row = 0x12345678;
char buf[4];
writeLittleEndianToBigEndian( &row, &buf, sizeof(row) );
其他回答
如果一个大端位32位无符号整数看起来像0xAABBCCDD,它等于2864434397,那么同样的32位无符号整数在小端位处理器上看起来像0xDDCCBBAA,它也等于2864434397。
如果一个大端序16位无符号空头看起来像0xAABB,它等于43707,那么同一个16位无符号空头在小端序处理器上看起来像0xBBAA,它也等于43707。
这里有两个方便的#define函数,用于将字节从小端序转换为大端序,反之亦然——>
// can be used for short, unsigned short, word, unsigned word (2-byte types)
#define BYTESWAP16(n) (((n&0xFF00)>>8)|((n&0x00FF)<<8))
// can be used for int or unsigned int or float (4-byte types)
#define BYTESWAP32(n) ((BYTESWAP16((n&0xFFFF0000)>>16))|((BYTESWAP16(n&0x0000FFFF))<<16))
// can be used for unsigned long long or double (8-byte types)
#define BYTESWAP64(n) ((BYTESWAP32((n&0xFFFFFFFF00000000)>>32))|((BYTESWAP32(n&0x00000000FFFFFFFF))<<32))
来这里寻找一个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,所以这不是一个选项。)
有一个叫做BSWAP的汇编指令可以帮你做交换,非常快。 你可以在这里阅读。
Visual Studio,或者更准确地说是Visual c++运行时库,为此提供了平台intrinsic,称为_byteswap_ushort()、_byteswap_ulong()和_byteswap_int64()。其他平台应该也有类似的情况,但我不知道它们会被称为什么。
认真……我不明白为什么所有的解决方案都那么复杂!最简单、最通用的模板函数如何?它可以在任何操作系统的任何情况下交换任何大小的任何类型????
template <typename T>
void SwapEnd(T& var)
{
static_assert(std::is_pod<T>::value, "Type must be POD type for safety");
std::array<char, sizeof(T)> varArray;
std::memcpy(varArray.data(), &var, sizeof(T));
for(int i = 0; i < static_cast<int>(sizeof(var)/2); i++)
std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
std::memcpy(&var, varArray.data(), sizeof(T));
}
这是C和c++结合的神奇力量!只需逐个字符交换原始变量。
要点1:没有操作符:请记住,我没有使用简单的赋值操作符“=”,因为当反转字节序时,一些对象将被打乱,复制构造函数(或赋值操作符)将不起作用。因此,一个字符一个字符地复制它们更加可靠。
Point 2: Be aware of alignment issues: Notice that we're copying to and from an array, which is the right thing to do because the C++ compiler doesn't guarantee that we can access unaligned memory (this answer was updated from its original form for this). For example, if you allocate uint64_t, your compiler cannot guarantee that you can access the 3rd byte of that as a uint8_t. Therefore, the right thing to do is to copy this to a char array, swap it, then copy it back (so no reinterpret_cast). Notice that compilers are mostly smart enough to convert what you did back to a reinterpret_cast if they're capable of accessing individual bytes regardless of alignment.
使用此函数:
double x = 5;
SwapEnd(x);
现在x的字节序不同了。
大多数平台都有一个系统头文件,提供了有效的byteswap函数。在Linux上是<end .h>。你可以用c++很好地包装它:
#include <iostream>
#include <endian.h>
template<size_t N> struct SizeT {};
#define BYTESWAPS(bits) \
template<class T> inline T htobe(T t, SizeT<bits / 8>) { return htobe ## bits(t); } \
template<class T> inline T htole(T t, SizeT<bits / 8>) { return htole ## bits(t); } \
template<class T> inline T betoh(T t, SizeT<bits / 8>) { return be ## bits ## toh(t); } \
template<class T> inline T letoh(T t, SizeT<bits / 8>) { return le ## bits ## toh(t); }
BYTESWAPS(16)
BYTESWAPS(32)
BYTESWAPS(64)
#undef BYTESWAPS
template<class T> inline T htobe(T t) { return htobe(t, SizeT<sizeof t>()); }
template<class T> inline T htole(T t) { return htole(t, SizeT<sizeof t>()); }
template<class T> inline T betoh(T t) { return betoh(t, SizeT<sizeof t>()); }
template<class T> inline T letoh(T t) { return letoh(t, SizeT<sizeof t>()); }
int main()
{
std::cout << std::hex;
std::cout << htobe(static_cast<unsigned short>(0xfeca)) << '\n';
std::cout << htobe(0xafbeadde) << '\n';
// Use ULL suffix to specify integer constant as unsigned long long
std::cout << htobe(0xfecaefbeafdeedfeULL) << '\n';
}
输出:
cafe
deadbeaf
feeddeafbeefcafe