如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
查找位移位,因为这基本上是所有你需要做的交换从小->大端dian。然后根据位的大小,改变位移位的方式。
其他回答
哇,我简直不敢相信我在这里读到的一些答案。实际上汇编中有一条指令比其他任何程序都快。bswap。你可以简单地写一个这样的函数…
__declspec(naked) uint32_t EndianSwap(uint32 value)
{
__asm
{
mov eax, dword ptr[esp + 4]
bswap eax
ret
}
}
它比之前提到的内在函数要快得多。我把它们拆开看了看。上面的函数没有序言/尾声,因此实际上没有任何开销。
unsigned long _byteswap_ulong(unsigned long value);
做16位同样容易,除了你会使用xchg al,啊。Bswap仅适用于32位寄存器。
64位有点棘手,但也不过分。比上面所有带有循环和模板的例子都要好得多。
这里有一些注意事项……首先,bswap只能在80x486以上的CPU上使用。有人打算在386上运行吗?!?如果是这样,你仍然可以用…替换bswap。
mov ebx, eax
shr ebx, 16
xchg al, ah
xchg bl, bh
shl eax, 16
or eax, ebx
内联汇编也只能在Visual Studio的x86代码中使用。裸函数不能内衬,而且在x64版本中也不可用。对于那个实例,你必须使用编译器的内在函数。
有一个叫做BSWAP的汇编指令可以帮你做交换,非常快。 你可以在这里阅读。
Visual Studio,或者更准确地说是Visual c++运行时库,为此提供了平台intrinsic,称为_byteswap_ushort()、_byteswap_ulong()和_byteswap_int64()。其他平台应该也有类似的情况,但我不知道它们会被称为什么。
大多数平台都有一个系统头文件,提供了有效的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
如果你正在使用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位交换就是位旋转。
顺便说一句,调用这些内在函数而不是调用自己的内在函数可以获得最好的性能和代码密度。
查找位移位,因为这基本上是所有你需要做的交换从小->大端dian。然后根据位的大小,改变位移位的方式。