如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
如何在c++中转换大端值和小端值?
为了清晰起见,我必须将二进制数据(双精度浮点值以及32位和64位整数)从一个CPU架构转换到另一个CPU架构。这并不涉及网络,因此ntoh()和类似的函数在这里不能工作。
注意:我接受的答案直接适用于我的目标编译器(这就是我选择它的原因)。然而,这里还有其他非常好的、更方便的答案。
当前回答
似乎安全的方法是在每个单词上使用“顿音”。所以,如果你有。
std::vector<uint16_t> storage(n); // where n is the number to be converted
// the following would do the trick
std::transform(word_storage.cbegin(), word_storage.cend()
, word_storage.begin(), [](const uint16_t input)->uint16_t {
return htons(input); });
如果您是在一个大端系统上,那么上面的代码将是一个无操作,因此我将查找您的平台使用的任何编译时条件,以确定htons是否是一个无操作。毕竟是O(n)在Mac上,它会是这样的……
#if (__DARWIN_BYTE_ORDER != __DARWIN_BIG_ENDIAN)
std::transform(word_storage.cbegin(), word_storage.cend()
, word_storage.begin(), [](const uint16_t input)->uint16_t {
return htons(input); });
#endif
其他回答
我有这个代码,允许我从HOST_ENDIAN_ORDER(无论它是什么)转换为LITTLE_ENDIAN_ORDER或BIG_ENDIAN_ORDER。我使用一个模板,所以如果我试图从HOST_ENDIAN_ORDER转换为LITTLE_ENDIAN_ORDER,他们恰好是相同的机器为我编译,不会生成任何代码。
下面是带有注释的代码:
// We define some constant for little, big and host endianess. Here I use
// BOOST_LITTLE_ENDIAN/BOOST_BIG_ENDIAN to check the host indianess. If you
// don't want to use boost you will have to modify this part a bit.
enum EEndian
{
LITTLE_ENDIAN_ORDER,
BIG_ENDIAN_ORDER,
#if defined(BOOST_LITTLE_ENDIAN)
HOST_ENDIAN_ORDER = LITTLE_ENDIAN_ORDER
#elif defined(BOOST_BIG_ENDIAN)
HOST_ENDIAN_ORDER = BIG_ENDIAN_ORDER
#else
#error "Impossible de determiner l'indianness du systeme cible."
#endif
};
// this function swap the bytes of values given it's size as a template
// parameter (could sizeof be used?).
template <class T, unsigned int size>
inline T SwapBytes(T value)
{
union
{
T value;
char bytes[size];
} in, out;
in.value = value;
for (unsigned int i = 0; i < size / 2; ++i)
{
out.bytes[i] = in.bytes[size - 1 - i];
out.bytes[size - 1 - i] = in.bytes[i];
}
return out.value;
}
// Here is the function you will use. Again there is two compile-time assertion
// that use the boost librarie. You could probably comment them out, but if you
// do be cautious not to use this function for anything else than integers
// types. This function need to be calles like this :
//
// int x = someValue;
// int i = EndianSwapBytes<HOST_ENDIAN_ORDER, BIG_ENDIAN_ORDER>(x);
//
template<EEndian from, EEndian to, class T>
inline T EndianSwapBytes(T value)
{
// A : La donnée à swapper à une taille de 2, 4 ou 8 octets
BOOST_STATIC_ASSERT(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8);
// A : La donnée à swapper est d'un type arithmetic
BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value);
// Si from et to sont du même type on ne swap pas.
if (from == to)
return value;
return SwapBytes<T, sizeof(T)>(value);
}
来这里寻找一个Boost解决方案,失望地离开,但最终在其他地方找到了它。你可以使用boost::endian::endian_reverse。它被模板化/重载了所有的基元类型:
#include <iostream>
#include <iomanip>
#include "boost/endian/conversion.hpp"
int main()
{
uint32_t word = 0x01;
std::cout << std::hex << std::setfill('0') << std::setw(8) << word << std::endl;
// outputs 00000001;
uint32_t word2 = boost::endian::endian_reverse(word);
// there's also a `void ::endian_reverse_inplace(...) function
// that reverses the value passed to it in place and returns nothing
std::cout << std::hex << std::setfill('0') << std::setw(8) << word2 << std::endl;
// outputs 01000000
return 0;
}
示范
虽然,看起来c++23最终用std::byteswap解决了这个问题。(我使用的是c++17,所以这不是一个选项。)
我们已经用模板做到了这一点。你可以这样做:
// 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);
}
和在C中一样:
short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));
您还可以声明一个无符号字符的向量,将输入值memcpy放入其中,将字节反向转换为另一个向量,然后将字节memcpy取出,但这将花费比旋转位长几个数量级的时间,特别是对于64位值。
这是我想到的一个通用版本,用于在适当的位置交换值。如果性能存在问题,其他建议会更好。
template<typename T>
void ByteSwap(T * p)
{
for (int i = 0; i < sizeof(T)/2; ++i)
std::swap(((char *)p)[i], ((char *)p)[sizeof(T)-1-i]);
}
免责声明:我还没有尝试编译或测试它。