如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
虽然没有使用固有函数有效,但肯定是可移植的。我的回答:
#include <cstdint>
#include <type_traits>
/**
* Perform an endian swap of bytes against a templatized unsigned word.
*
* @tparam value_type The data type to perform the endian swap against.
* @param value The data value to swap.
*
* @return value_type The resulting swapped word.
*/
template <typename value_type>
constexpr inline auto endian_swap(value_type value) -> value_type
{
using half_type = typename std::conditional<
sizeof(value_type) == 8u,
uint32_t,
typename std::conditional<sizeof(value_type) == 4u, uint16_t, uint8_t>::
type>::type;
size_t const half_bits = sizeof(value_type) * 8u / 2u;
half_type const upper_half = static_cast<half_type>(value >> half_bits);
half_type const lower_half = static_cast<half_type>(value);
if (sizeof(value_type) == 2u)
{
return (static_cast<value_type>(lower_half) << half_bits) | upper_half;
}
return ((static_cast<value_type>(endian_swap(lower_half)) << half_bits) |
endian_swap(upper_half));
}
其他回答
摘自Rob Pike的《字节顺序谬误》:
假设数据流有一个小端编码的32位整数。下面是如何提取它(假设无符号字节):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | ((unsigned)data[3]<<24);
如果它是big-endian,下面是如何提取它:
i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | ((unsigned)data[0]<<24);
TL;DR:不要担心你的平台原生顺序,重要的是你从中读取的流的字节顺序,你最好希望它是定义良好的。
注1:这里int和unsigned int是32位,否则类型可能需要调整。
注2:最后一个字节必须在移位前显式转换为unsigned,因为默认情况下它被提升为int,移位24位意味着操作符号位,这是未定义行为。
我真的很惊讶没有人提到htobeXX和betohXX函数。它们定义在end .h中,非常类似于网络函数htonXX。
有一个叫做BSWAP的汇编指令可以帮你做交换,非常快。 你可以在这里阅读。
Visual Studio,或者更准确地说是Visual c++运行时库,为此提供了平台intrinsic,称为_byteswap_ushort()、_byteswap_ulong()和_byteswap_int64()。其他平台应该也有类似的情况,但我不知道它们会被称为什么。
如果一个大端位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))
虽然没有使用固有函数有效,但肯定是可移植的。我的回答:
#include <cstdint>
#include <type_traits>
/**
* Perform an endian swap of bytes against a templatized unsigned word.
*
* @tparam value_type The data type to perform the endian swap against.
* @param value The data value to swap.
*
* @return value_type The resulting swapped word.
*/
template <typename value_type>
constexpr inline auto endian_swap(value_type value) -> value_type
{
using half_type = typename std::conditional<
sizeof(value_type) == 8u,
uint32_t,
typename std::conditional<sizeof(value_type) == 4u, uint16_t, uint8_t>::
type>::type;
size_t const half_bits = sizeof(value_type) * 8u / 2u;
half_type const upper_half = static_cast<half_type>(value >> half_bits);
half_type const lower_half = static_cast<half_type>(value);
if (sizeof(value_type) == 2u)
{
return (static_cast<value_type>(lower_half) << half_bits) | upper_half;
}
return ((static_cast<value_type>(endian_swap(lower_half)) << half_bits) |
endian_swap(upper_half));
}