Uha*_*all 187 c++ endianness
如何在C++中的big-endian和little-endian值之间进行转换?
编辑:为清楚起见,我必须将二进制数据(双精度浮点值和32位和64位整数)从一个CPU架构转换为另一个CPU架构.这不涉及网络,因此ntoh()和类似的功能在这里不起作用.
编辑#2:我接受的答案直接适用于我正在编制的编译器(这就是我选择它的原因).但是,这里有其他非常好的,更便携的答案.
Nil*_*nck 159
如果您使用的是Visual C++,请执行以下操作:包含intrin.h并调用以下函数:
对于16位数字:
unsigned short _byteswap_ushort(unsigned short value);
Run Code Online (Sandbox Code Playgroud)
对于32位数字:
unsigned long _byteswap_ulong(unsigned long value);
Run Code Online (Sandbox Code Playgroud)
对于64位数字:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
Run Code Online (Sandbox Code Playgroud)
不需要转换8位数字(字符).
此外,这些仅针对无符号值定义,它们也适用于有符号整数.
对于浮点数和双精度数,它比普通整数更难,因为这些可能与否可能在主机的字节顺序中.您可以在big-endian机器上获得little-endian浮点数,反之亦然.
其他编译器也有类似的内在函数.
以GCC为例,您可以直接致电:
int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)
Run Code Online (Sandbox Code Playgroud)
(不需要包含某些内容).Afaik bits.h也以非gcc为中心的方式声明了相同的功能.
16位交换它只是一个位旋转.
调用内在函数而不是自己编辑可以获得最佳的性能和代码密度.
Ale*_* C. 78
简单的说:
#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;
}
Run Code Online (Sandbox Code Playgroud)
用法:swap_endian<uint32_t>(42).
Mat*_* M. 70
来自Rob Pyke 的Byte Order Fallacy:
假设您的数据流具有小端编码的32位整数.以下是如何提取它(假设无符号字节):
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
Run Code Online (Sandbox Code Playgroud)
如果它是big-endian,这里是如何提取它:
i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);
Run Code Online (Sandbox Code Playgroud)
TL; DR:不要担心你的平台本机顺序,所有重要的是你正在阅读的流的字节顺序,你最好希望它的定义很好.
注意:在评论中注释到没有显式类型转换,重要的是data成为unsigned char或者数组uint8_t.使用signed char或char(如果有符号)将导致data[x]被提升为整数并data[x] << 24可能将1移入符号位UB.
Fro*_*sty 49
如果您出于网络/主机兼容性的目的这样做,您应该使用:
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)
Run Code Online (Sandbox Code Playgroud)
如果您出于某种其他原因而执行此操作,则此处提供的byte_swap解决方案之一可以正常工作.
Ste*_*mer 25
我从这篇文章中提出了一些建议并将它们组合在一起形成:
#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);
}
Run Code Online (Sandbox Code Playgroud)
Kev*_*vin 16
从big-endian到little-endian的过程与从little-endian到big-endian的过程相同.
这是一些示例代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ark 12
我们用模板完成了这个.你可以这样:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
和你在C中做的一样:
short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));
Run Code Online (Sandbox Code Playgroud)
您还可以声明一个无符号字符向量,将输入值memcpy到其中,将字节转换为另一个向量并将字节记忆输出,但这将比bit-twiddling长几个数量级,特别是对于64位值.
在大多数POSIX系统上(通过它不在POSIX标准中)有endian.h,它可用于确定系统使用的编码.从那里它是这样的:
unsigned int change_endian(unsigned int x)
{
unsigned char *ptr = (unsigned char *)&x;
return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
}
Run Code Online (Sandbox Code Playgroud)
这交换顺序(从big-endian到little endian):
如果您的数字为0xDEADBEEF(在小端系统上存储为0xEFBEADDE),则ptr [0]将为0xEF,ptr [1]为0xBE等.
但是如果你想将它用于网络,那么htons,htonl和htonll(以及它们的反向ntohs,ntohl和ntohll)将有助于从主机顺序转换为网络顺序.
大多数平台都有一个系统头文件,可提供高效的字节交换功能。在 Linux 上,它位于<endian.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';
}
Run Code Online (Sandbox Code Playgroud)
输出:
cafe
deadbeaf
feeddeafbeefcafe
Run Code Online (Sandbox Code Playgroud)
请注意,至少对于Windows,htonl()比它们的内部对应物_byteswap_ulong()要慢得多.前者是一个DLL库调用ws2_32.dll,后者是一个BSWAP汇编指令.因此,如果您正在编写一些与平台相关的代码,请更喜欢使用内在函数来提高速度:
#define htonl(x) _byteswap_ulong(x)
Run Code Online (Sandbox Code Playgroud)
这对于.PNG图像处理尤为重要,其中所有整数都保存在Big Endian中,并解释"一个人可以使用htonl()..."{以减慢典型的Windows程序,如果你没有准备好}.
说真的......我不明白为什么所有解决方案都那么复杂!在任何操作系统中,在任何情况下交换任何类型的任何类型的最简单,最通用的模板函数怎么样?
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));
}
Run Code Online (Sandbox Code Playgroud)
这是C和C++的神奇力量!只需按字符交换原始变量.
请记住,我没有使用简单赋值运算符"=",因为当翻转字节序并且复制构造函数(或赋值运算符)不起作用时,某些对象将被搞砸.因此,通过char复制它们更可靠.
要打电话,只需使用
double x = 5;
SwapEnd(x);
Run Code Online (Sandbox Code Playgroud)
现在uint64_t在字节序上有所不同.