如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
如果你这样做是为了网络/主机兼容性,你应该使用:
ntohl() //Network to Host byte order (Long)
htonl() //Host to Network byte order (Long)
ntohs() //Network to Host byte order (Short)
htons() //Host to Network byte order (Short)
如果是出于其他原因,这里提供的byte_swap解决方案之一也可以很好地工作。
其他回答
我们已经用模板做到了这一点。你可以这样做:
// 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);
}
查找位移位,因为这基本上是所有你需要做的交换从小->大端dian。然后根据位的大小,改变位移位的方式。
摘自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位意味着操作符号位,这是未定义行为。
我从这篇文章中得到了一些建议,并把它们放在一起形成了这个:
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>
#include <cstdint>
enum endianness
{
little_endian,
big_endian,
network_endian = big_endian,
#if defined(BOOST_LITTLE_ENDIAN)
host_endian = little_endian
#elif defined(BOOST_BIG_ENDIAN)
host_endian = big_endian
#else
#error "unable to determine system endianness"
#endif
};
namespace detail {
template<typename T, size_t sz>
struct swap_bytes
{
inline T operator()(T val)
{
throw std::out_of_range("data size");
}
};
template<typename T>
struct swap_bytes<T, 1>
{
inline T operator()(T val)
{
return val;
}
};
template<typename T>
struct swap_bytes<T, 2>
{
inline T operator()(T val)
{
return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
}
};
template<typename T>
struct swap_bytes<T, 4>
{
inline T operator()(T val)
{
return ((((val) & 0xff000000) >> 24) |
(((val) & 0x00ff0000) >> 8) |
(((val) & 0x0000ff00) << 8) |
(((val) & 0x000000ff) << 24));
}
};
template<>
struct swap_bytes<float, 4>
{
inline float operator()(float val)
{
uint32_t mem =swap_bytes<uint32_t, sizeof(uint32_t)>()(*(uint32_t*)&val);
return *(float*)&mem;
}
};
template<typename T>
struct swap_bytes<T, 8>
{
inline T operator()(T val)
{
return ((((val) & 0xff00000000000000ull) >> 56) |
(((val) & 0x00ff000000000000ull) >> 40) |
(((val) & 0x0000ff0000000000ull) >> 24) |
(((val) & 0x000000ff00000000ull) >> 8 ) |
(((val) & 0x00000000ff000000ull) << 8 ) |
(((val) & 0x0000000000ff0000ull) << 24) |
(((val) & 0x000000000000ff00ull) << 40) |
(((val) & 0x00000000000000ffull) << 56));
}
};
template<>
struct swap_bytes<double, 8>
{
inline double operator()(double val)
{
uint64_t mem =swap_bytes<uint64_t, sizeof(uint64_t)>()(*(uint64_t*)&val);
return *(double*)&mem;
}
};
template<endianness from, endianness to, class T>
struct do_byte_swap
{
inline T operator()(T value)
{
return swap_bytes<T, sizeof(T)>()(value);
}
};
// specialisations when attempting to swap to the same endianess
template<class T> struct do_byte_swap<little_endian, little_endian, T> { inline T operator()(T value) { return value; } };
template<class T> struct do_byte_swap<big_endian, big_endian, T> { inline T operator()(T value) { return value; } };
} // namespace detail
template<endianness from, endianness to, class T>
inline T byte_swap(T value)
{
// ensure the data is only 1, 2, 4 or 8 bytes
BOOST_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
// ensure we're only swapping arithmetic types
BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);
return detail::do_byte_swap<from, to, T>()(value);
}
然后你可以这样使用它:
// swaps val from host-byte-order to network-byte-order
auto swapped = byte_swap<host_endian, network_endian>(val);
反之亦然
// swap a value received from the network into host-byte-order
auto val = byte_swap<network_endian, host_endian>(val_from_network);
使用下面的代码,您可以轻松地在BigEndian和LittleEndian之间进行切换
#define uint32_t unsigned
#define uint16_t unsigned short
#define swap16(x) ((((uint16_t)(x) & 0x00ff)<<8)| \
(((uint16_t)(x) & 0xff00)>>8))
#define swap32(x) ((((uint32_t)(x) & 0x000000ff)<<24)| \
(((uint32_t)(x) & 0x0000ff00)<<8)| \
(((uint32_t)(x) & 0x00ff0000)>>8)| \
(((uint32_t)(x) & 0xff000000)>>24))