如何在 C++ 中的 big-endian 和 little-endian 值之间进行转换?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/105252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I convert between big-endian and little-endian values in C++?
提问by Uhall
How do I convert between big-endian and little-endian values in C++?
如何在 C++ 中的 big-endian 和 little-endian 值之间进行转换?
EDIT: For clarity, I have to translate binary data (double-precision floating point values and 32-bit and 64-bit integers) from one CPU architecture to another. This doesn't involve networking, so ntoh() and similar functions won't work here.
编辑:为清楚起见,我必须将二进制数据(双精度浮点值以及 32 位和 64 位整数)从一种 CPU 架构转换为另一种架构。这不涉及网络,所以 ntoh() 和类似的函数在这里不起作用。
EDIT #2: The answer I accepted applies directly to compilers I'm targetting (which is why I chose it). However, there are other very good, more portable answers here.
编辑#2:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。但是,这里还有其他非常好的、更便携的答案。
采纳答案by Nils Pipenbrinck
If you're using Visual C++do the following: You include intrin.h and call the following functions:
如果您使用的是Visual C++,请执行以下操作: 包含 intrin.h 并调用以下函数:
For 16 bit numbers:
对于 16 位数字:
unsigned short _byteswap_ushort(unsigned short value);
For 32 bit numbers:
对于 32 位数字:
unsigned long _byteswap_ulong(unsigned long value);
For 64 bit numbers:
对于 64 位数字:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
8 bit numbers (chars) don't need to be converted.
8 位数字(字符)不需要转换。
Also these are only defined for unsigned values they work for signed integers as well.
此外,这些仅针对无符号值定义,它们也适用于有符号整数。
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order. You can get little-endian floats on big-endian machines and vice versa.
对于浮点数和双精度数,它与普通整数一样困难,因为它们可能在主机字节顺序中,也可能不在。您可以在大端机器上获得小端浮点数,反之亦然。
Other compilers have similar intrinsics as well.
其他编译器也有类似的内在函数。
In GCCfor example you can directly call some builtins as documented here:
例如,在GCC 中,您可以直接调用此处记录的一些内置函数:
uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)
(no need to include something). Afaik bits.h declares the same function in a non gcc-centric way as well.
(无需包含某些内容)。Afaik bits.h 也以非以 gcc 为中心的方式声明了相同的函数。
16 bit swap it's just a bit-rotate.
16 位交换它只是位旋转。
Calling the intrinsics instead of rolling your own gives you the best performance and code density btw..
调用内部函数而不是滚动自己的内部函数可以为您提供最佳性能和代码密度 btw..
回答by Alexandre C.
Simply put:
简单的说:
#include <climits>
template <typename T>
T swap_endian(T u)
{
static_assert (CHAR_BIT == 8, "CHAR_BIT != 8");
union
{
T u;
unsigned char u8[sizeof(T)];
} source, dest;
source.u = u;
for (size_t k = 0; k < sizeof(T); k++)
dest.u8[k] = source.u8[sizeof(T) - k - 1];
return dest.u;
}
usage: swap_endian<uint32_t>(42)
.
用法:swap_endian<uint32_t>(42)
。
回答by Matthieu M.
From The Byte Order Fallacyby Rob Pike:
来自Rob Pike的字节顺序谬误:
Let's say your data stream has a little-endian-encoded 32-bit integer. Here's how to extract it (assuming unsigned bytes):
假设您的数据流有一个小端编码的 32 位整数。以下是提取它的方法(假设是无符号字节):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
If it's big-endian, here's how to extract it:
如果是 big-endian,这里是提取它的方法:
i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);
TL;DR:don't worry about your platform native order, all that counts is the byte order of the stream your are reading from, and you better hope it's well defined.
TL;DR:不要担心您的平台本机顺序,所有重要的是您正在读取的流的字节顺序,您最好希望它定义明确。
Note: it was remarked in the comment that absent explicit type conversion, it was important that data
be an array of unsigned char
or uint8_t
. Using signed char
or char
(if signed) will result in data[x]
being promoted to an integer and data[x] << 24
potentially shifting a 1 into the sign bit which is UB.
注意:注释中指出,如果没有显式类型转换,重要data
的是unsigned char
or的数组uint8_t
。使用signed char
or char
(如果有符号)将导致data[x]
被提升为整数并data[x] << 24
可能将 1 移入符号位,即 UB。
回答by Frosty
If you are doing this for purposes of network/host compatability you should use:
如果您这样做是为了网络/主机兼容性,您应该使用:
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)
If you are doing this for some other reason one of the byte_swap solutions presented here would work just fine.
如果您出于某种其他原因执行此操作,则此处介绍的 byte_swap 解决方案之一可以正常工作。
回答by Steve Lorimer
I took a few suggestions from this post and put them together to form this:
我从这篇文章中得到了一些建议,并将它们组合在一起形成了这个:
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/endian.hpp>
#include <stdexcept>
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);
}
回答by Kevin
The procedure for going from big-endian to little-endian is the same as going from little-endian to big-endian.
从 big-endian 到 little-endian 的过程与从 little-endian 到 big-endian 的过程相同。
Here's some example code:
下面是一些示例代码:
void swapByteOrder(unsigned short& us)
{
us = (us >> 8) |
(us << 8);
}
void swapByteOrder(unsigned int& ui)
{
ui = (ui >> 24) |
((ui<<8) & 0x00FF0000) |
((ui>>8) & 0x0000FF00) |
(ui << 24);
}
void swapByteOrder(unsigned long long& ull)
{
ull = (ull >> 56) |
((ull<<40) & 0x00FF000000000000) |
((ull<<24) & 0x0000FF0000000000) |
((ull<<8) & 0x000000FF00000000) |
((ull>>8) & 0x00000000FF000000) |
((ull>>24) & 0x0000000000FF0000) |
((ull>>40) & 0x000000000000FF00) |
(ull << 56);
}
回答by anon6439
There is an assembly instruction called BSWAP that will do the swap for you, extremely fast. You can read about it here.
有一个名为 BSWAP 的汇编指令可以为您进行交换,速度非常快。你可以在这里阅读它。
Visual Studio, or more precisely the Visual C++ runtime library, has platform intrinsics for this, called _byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. Similar should exist for other platforms, but I'm not aware of what they would be called.
Visual Studio,或者更准确地说是 Visual C++ 运行时库,为此具有平台内部函数,称为_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64()
. 其他平台也应该存在类似的情况,但我不知道它们会被称为什么。
回答by Mark
We've done this with templates. You could do something like this:
我们已经用模板做到了这一点。你可以这样做:
// 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);
}
回答by Andrew
If you're doing this to transfer data between different platforms look at the ntoh and hton functions.
如果您这样做是为了在不同平台之间传输数据,请查看 ntoh 和 hton 函数。
回答by Ben Straub
The same way you do in C:
和你在 C 中做的一样:
short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));
You could also declare a vector of unsigned chars, memcpy the input value into it, reverse the bytes into another vector and memcpy the bytes out, but that'll take orders of magnitude longer than bit-twiddling, especially with 64-bit values.
您还可以声明一个无符号字符向量,将输入值 memcpy 到其中,将字节反转为另一个向量,然后将字节 memcpy 输出,但这将比 bit-twiddling 花费几个数量级的时间,尤其是对于 64 位值。