234

如何在 C++ 中的大端和小端值之间进行转换?

为清楚起见,我必须将二进制数据(双精度浮点值以及 32 位和 64 位整数)从一种 CPU 架构转换为另一种。这不涉及网络,因此 ntoh() 和类似的功能在这里不起作用。


注意:我接受的答案直接适用于我所针对的编译器(这就是我选择它的原因)。但是,这里还有其他非常好的、更便携的答案。

4

34 回答 34

200

如果您使用Visual C++,请执行以下操作:包含 intrin.h 并调用以下函数:

对于 16 位数字:

unsigned short _byteswap_ushort(unsigned short value);

对于 32 位数字:

unsigned long _byteswap_ulong(unsigned long value);

对于 64 位数字:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 位数字(字符)不需要转换。

此外,这些仅针对无符号值定义,它们也适用于有符号整数。

对于浮点数和双精度数,与普通整数一样困难,因为它们可能会或不会在主机字节顺序中。您可以在大端机器上获得小端浮点数,反之亦然。

其他编译器也有类似的内在函数。

例如,在GCC中,您可以直接调用此处记录的一些内置函数:

uint32_t __builtin_bswap32 (uint32_t x)
uint64_t __builtin_bswap64 (uint64_t x)

(无需包含某些内容)。Afaik bits.h 也以非 gcc 为中心的方式声明了相同的函数。

16位交换它只是一个位旋转。

调用内部函数而不是自己滚动可以为您提供最佳性能和代码密度顺便说一句..

于 2008-09-19T20:31:38.887 回答
102

简单的说:

#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;
}

用法:swap_endian<uint32_t>(42)

于 2011-02-10T11:27:13.820 回答
86

来自Rob Pike的字节顺序谬误

假设您的数据流有一个小端编码的 32 位整数。以下是如何提取它(假设无符号字节):

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

如果它是大端的,这里是如何提取它:

i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

TL;DR:不要担心您的平台本机顺序,重要的是您正在读取的流的字节顺序,您最好希望它定义明确。

注意:在注释中指出,如果没有显式类型转换,重要的是要成为ordata的数组。使用或(如果有符号)将导致被提升为整数并可能将 1 移入符号位,即 UB。unsigned charuint8_tsigned charchardata[x]data[x] << 24

于 2012-04-27T06:51:22.537 回答
59

如果您出于网络/主机兼容性的目的这样做,您应该使用:

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 解决方案之一可以正常工作。

于 2008-09-19T20:38:18.090 回答
28

我从这篇文章中得到了一些建议,并将它们组合在一起形成了这个:

#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);
于 2010-08-19T14:36:13.650 回答
17

从大端到小端的过程与从小端到大端的过程相同。

这是一些示例代码:

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);
}
于 2008-09-19T20:31:54.567 回答
16

有一个名为 BSWAP 的汇编指令会为您进行交换,速度非常快。你可以在这里阅读。

Visual Studio,或更准确地说是 Visual C++ 运行时库,为此具有平台内在函数,称为_byteswap_ushort(), _byteswap_ulong(), and _byteswap_int64(). 其他平台也应该存在类似的情况,但我不知道它们会被称为什么。

于 2008-09-19T20:34:46.913 回答
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);
}
于 2008-09-19T20:29:23.433 回答
8

如果您这样做是为了在不同平台之间传输数据,请查看 ntoh 和 hton 函数。

于 2008-09-19T20:26:48.197 回答
8

与您在 C 中执行的方式相同:

short big = 0xdead;
short little = (((big & 0xff)<<8) | ((big & 0xff00)>>8));

您还可以声明一个无符号字符向量,将输入值 memcpy 到其中,将字节反转为另一个向量并将字节 memcpy 出来,但这将比位旋转要长几个数量级,尤其是对于 64 位值。

于 2008-09-19T20:30:07.517 回答
8

在大多数 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];
}

这交换了顺序(从大端到小端):

如果您有数字 0xDEADBEEF(在存储为 0xEFBEADDE 的小端系统上),ptr[0] 将为 0xEF,ptr[1] 为 0xBE,等等。

但是如果你想将它用于网络,那么 htons、htonl 和 htonll(以及它们的倒数 ntohs、ntohl 和 ntohll)将有助于从主机顺序转换为网络顺序。

于 2008-09-19T20:33:14.327 回答
7

请注意,至少对于 Windows,htonl() 比它们固有的对应 _byteswap_ulong() 慢得多。前者是对ws2_32.dll 的DLL 库调用,后者是一条BSWAP 汇编指令。因此,如果您正在编写一些依赖于平台的代码,则更喜欢使用内部函数来提高速度:

#define htonl(x) _byteswap_ulong(x)

这对于 .PNG 图像处理可能特别重要,其中所有整数都保存在 Big Endian 中,并说明“可以使用 htonl()...”{如果您没有准备好,可以减慢典型的 Windows 程序的速度}。

于 2013-08-20T10:45:38.080 回答
5

大多数平台都有一个提供高效字节交换功能的系统头文件。在 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';
}

输出:

cafe
deadbeaf
feeddeafbeefcafe
于 2011-02-10T12:02:30.463 回答
5

说真的......我不明白为什么所有的解决方案都那么复杂在任何操作系统中的任何情况下交换任何类型的任何大小的最简单、最通用的模板函数怎么样????

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));
}

这就是 C 和 C++ 的魔力!只需逐个字符交换原始变量。

第 1 点:无运算符:请记住,我没有使用简单的赋值运算符“=”,因为当翻转字节序时某些对象会被弄乱,并且复制构造函数(或赋值运算符)将不起作用。因此,逐个字符地复制它们更可靠。

第 2 点:注意对齐问题:请注意,我们正在向数组复制和从数组复制,这是正确的做法,因为 C++ 编译器不保证我们可以访问未对齐的内存(此答案已从其原始更新为此的形式)。例如,如果你 allocate uint64_t,你的编译器不能保证你可以访问它的第 3 个字节作为uint8_t. 因此,正确的做法是将其复制到 char 数组,交换它,然后将其复制回来(所以 no reinterpret_cast)。reinterpret_cast请注意,如果编译器能够访问单个字节而不管对齐方式,编译器大多足够聪明,可以将您所做的转换回 a 。

要使用此功能

double x = 5;
SwapEnd(x);

现在x在字节序上有所不同。

于 2014-08-07T08:07:29.387 回答
4

我有这段代码允许我从 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);
}
于 2008-09-20T04:25:11.150 回答
3

我喜欢这个,只是为了风格:-)

long swap(long i) {
    char *c = (char *) &i;
    return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
}
于 2011-06-26T22:13:17.547 回答
3

如果一个大端序的 32 位无符号整数看起来像等于 2864434397 的 0xAABBCCDD,那么同样的 32 位无符号整数在一个也等于 2864434397 的小端序处理器上看起来像 0xDDCCBBAA。

如果一个 big-endian 16 位 unsigned short 看起来像等于 43707 的 0xAABB,那么同样的 16 位 unsigned short 在一个也等于 43707 的 little-endian 处理器上看起来像 0xBBAA。

这里有几个方便的#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))
于 2014-09-06T21:06:09.760 回答
3

只是想我在这里添加了自己的解决方案,因为我在任何地方都没有看到它。它是一个小型且可移植的 C++ 模板化函数,可移植,仅使用位操作。

template<typename T> inline static T swapByteOrder(const T& val) {
    int totalBytes = sizeof(val);
    T swapped = (T) 0;
    for (int i = 0; i < totalBytes; ++i) {
        swapped |= (val >> (8*(totalBytes-i-1)) & 0xFF) << (8*i);
    }
    return swapped;
}
于 2016-01-29T19:30:25.850 回答
2

这是我想出的一个通用版本,用于交换适当的值。如果性能有问题,其他建议会更好。

 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]);
    }

免责声明:我还没有尝试编译或测试它。

于 2008-09-19T21:07:01.627 回答
2

如果您采用通用模式来反转单词中的位顺序,并剔除反转每个字节中位的部分,那么您将得到仅反转单词中字节的部分。对于 64 位:

x = ((x & 0x00000000ffffffff) << 32) ^ ((x >> 32) & 0x00000000ffffffff);
x = ((x & 0x0000ffff0000ffff) << 16) ^ ((x >> 16) & 0x0000ffff0000ffff);
x = ((x & 0x00ff00ff00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff00ff00ff);

编译器应该清除多余的位屏蔽操作(我将它们保留以突出显示模式),但如果没有,您可以这样重写第一行:

x = ( x                       << 32) ^  (x >> 32);

在大多数架构上,这通常应该简化为一条循环指令(忽略整个操作可能是一条指令)。

在 RISC 处理器上,大而复杂的常量可能会导致编译器困难。不过,您可以从前一个常数中轻松计算出每个常数。像这样:

uint64_t k = 0x00000000ffffffff; /* compiler should know a trick for this */
x = ((x & k) << 32) ^ ((x >> 32) & k);
k ^= k << 16;
x = ((x & k) << 16) ^ ((x >> 16) & k);
k ^= k << 8;
x = ((x & k) <<  8) ^ ((x >>  8) & k);

如果你愿意,你可以把它写成一个循环。它不会有效率,但只是为了好玩:

int i = sizeof(x) * CHAR_BIT / 2;
uintmax_t k = (1 << i) - 1;
while (i >= 8)
{
    x = ((x & k) << i) ^ ((x >> i) & k);
    i >>= 1;
    k ^= k << i;
}

为了完整起见,这里是第一种形式的简化 32 位版本:

x = ( x               << 16) ^  (x >> 16);
x = ((x & 0x00ff00ff) <<  8) ^ ((x >>  8) & 0x00ff00ff);
于 2013-08-20T11:45:02.260 回答
2

哇,我简直不敢相信我在这里读到的一些答案。实际上,汇编中有一条指令比其他任何指令都快。交换。你可以简单地写一个这样的函数......

__declspec(naked) uint32_t EndianSwap(uint32 value)
{
    __asm
    {
        mov eax, dword ptr[esp + 4]
        bswap eax
        ret
    }
}

它比建议的内在函数快得多我把它们拆开看了看。上述功能没有序言/尾声,因此几乎没有任何开销。

unsigned long _byteswap_ulong(unsigned long value);

做 16 位同样容易,除了你会使用 xchg al,啊。bswap 仅适用于 32 位寄存器。

64 位有点棘手,但并不过分。比上面所有带有循环和模板等的示例要好得多。

这里有一些注意事项...首先 bswap 仅适用于 80x486 CPU 及更高版本。有人打算在 386 上运行它吗?!?如果是这样,您仍然可以将 bswap 替换为...

mov ebx, eax
shr ebx, 16
xchg al, ah
xchg bl, bh
shl eax, 16
or eax, ebx

此外,内联汇编仅在 Visual Studio 的 x86 代码中可用。裸函数不能被衬里,并且在 x64 版本中也不可用。在那个实例中,您将不得不使用编译器内在函数。

于 2014-09-24T01:47:38.117 回答
2

使用下面的代码,您可以轻松地在 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))
于 2016-09-17T15:58:44.057 回答
2

我真的很惊讶没有人提到 htobeXX 和 betohXX 功能。它们在 endian.h 中定义,与网络函数 htonXX 非常相似。

于 2017-05-11T19:15:28.647 回答
1

我最近在 C 中编写了一个宏来执行此操作,但它在 C++ 中同样有效:

#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
    ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)

它接受任何类型并反转传递参数中的字节。示例用法:

int main(){
    unsigned long long x = 0xABCDEF0123456789;
    printf("Before: %llX\n",x);
    REVERSE_BYTES(x);
    printf("After : %llX\n",x);

    char c[7]="nametag";
    printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
    REVERSE_BYTES(c);
    printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}

哪个打印:

Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman

以上是完全可以复制/粘贴的,但是这里有很多事情要做,所以我将逐个分解它是如何工作的:

首先值得注意的是整个宏被封装在一个do while(0)块中。这是一个常见的习惯用法,允许在宏之后使用正常的分号。

接下来是使用一个名为REVERSE_BYTES循环for计数器的变量。宏本身的名称用作变量名称,以确保它不会与使用宏的范围内可能存在的任何其他符号冲突。由于该名称在宏的扩展中使用,因此在此处用作变量名时不会再次扩展。

for循环中,有两个字节被引用和异或交换(因此不需要临时变量名):

((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]

__VA_ARGS__表示赋予宏的任何内容,并用于增加可能传入的内容的灵活性(尽管幅度不大)。然后获取该参数的地址并将其转换为一个unsigned char指针,以允许通过数组[]下标交换其字节。

最后一个特殊点是没有{}大括号。它们不是必需的,因为每个交换中的所有步骤都与逗号运算符连接,使它们成为一个语句。

最后,值得注意的是,如果速度是重中之重,这不是理想的方法。如果这是一个重要因素,那么其他答案中引用的某些特定于类型的宏或特定于平台的指令可能是更好的选择。然而,这种方法可移植到所有类型、所有主要平台以及 C 和 C++ 语言。

于 2016-12-17T07:15:36.217 回答
1

如果您有 C++ 17,则添加此标头

#include <algorithm>

使用此模板函数交换字节:

template <typename T>
void swapEndian(T& buffer)
{
    static_assert(std::is_pod<T>::value, "swapEndian support POD type only");
    char* startIndex = static_cast<char*>((void*)buffer.data());
    char* endIndex = startIndex + sizeof(buffer);
    std::reverse(startIndex, endIndex);
}

像这样称呼它:

swapEndian (stlContainer);
于 2020-04-10T18:42:59.707 回答
0

用于实现优化器友好的未对齐非就地字节序访问器的便携式技术。它们适用于每个编译器、每个边界对齐和每个字节排序。根据本机字节序和对齐方式,对这些未对齐的例程进行补充或讨论。部分上市,但你明白了。BO* 是基于本机字节顺序的常量值。

uint32_t sw_get_uint32_1234(pu32)
uint32_1234 *pu32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32_1234[0] = (*pu32)[BO32_0];
  bou32.u32_1234[1] = (*pu32)[BO32_1];
  bou32.u32_1234[2] = (*pu32)[BO32_2];
  bou32.u32_1234[3] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_1234(pu32, u32)
uint32_1234 *pu32;
uint32_t u32;
{
  union {
    uint32_1234 u32_1234;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_1234[0];
  (*pu32)[BO32_1] = bou32.u32_1234[1];
  (*pu32)[BO32_2] = bou32.u32_1234[2];
  (*pu32)[BO32_3] = bou32.u32_1234[3];
}

#if HAS_SW_INT64
int64 sw_get_int64_12345678(pi64)
int64_12345678 *pi64;
{
  union {
    int64_12345678 i64_12345678;
    int64 i64;
  } boi64;
  boi64.i64_12345678[0] = (*pi64)[BO64_0];
  boi64.i64_12345678[1] = (*pi64)[BO64_1];
  boi64.i64_12345678[2] = (*pi64)[BO64_2];
  boi64.i64_12345678[3] = (*pi64)[BO64_3];
  boi64.i64_12345678[4] = (*pi64)[BO64_4];
  boi64.i64_12345678[5] = (*pi64)[BO64_5];
  boi64.i64_12345678[6] = (*pi64)[BO64_6];
  boi64.i64_12345678[7] = (*pi64)[BO64_7];
  return(boi64.i64);
}
#endif

int32_t sw_get_int32_3412(pi32)
int32_3412 *pi32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32_3412[2] = (*pi32)[BO32_0];
  boi32.i32_3412[3] = (*pi32)[BO32_1];
  boi32.i32_3412[0] = (*pi32)[BO32_2];
  boi32.i32_3412[1] = (*pi32)[BO32_3];
  return(boi32.i32);
}

void sw_set_int32_3412(pi32, i32)
int32_3412 *pi32;
int32_t i32;
{
  union {
    int32_3412 i32_3412;
    int32_t i32;
  } boi32;
  boi32.i32 = i32;
  (*pi32)[BO32_0] = boi32.i32_3412[2];
  (*pi32)[BO32_1] = boi32.i32_3412[3];
  (*pi32)[BO32_2] = boi32.i32_3412[0];
  (*pi32)[BO32_3] = boi32.i32_3412[1];
}

uint32_t sw_get_uint32_3412(pu32)
uint32_3412 *pu32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32_3412[2] = (*pu32)[BO32_0];
  bou32.u32_3412[3] = (*pu32)[BO32_1];
  bou32.u32_3412[0] = (*pu32)[BO32_2];
  bou32.u32_3412[1] = (*pu32)[BO32_3];
  return(bou32.u32);
}

void sw_set_uint32_3412(pu32, u32)
uint32_3412 *pu32;
uint32_t u32;
{
  union {
    uint32_3412 u32_3412;
    uint32_t u32;
  } bou32;
  bou32.u32 = u32;
  (*pu32)[BO32_0] = bou32.u32_3412[2];
  (*pu32)[BO32_1] = bou32.u32_3412[3];
  (*pu32)[BO32_2] = bou32.u32_3412[0];
  (*pu32)[BO32_3] = bou32.u32_3412[1];
}

float sw_get_float_1234(pf)
float_1234 *pf;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f_1234[0] = (*pf)[BO32_0];
  bof.f_1234[1] = (*pf)[BO32_1];
  bof.f_1234[2] = (*pf)[BO32_2];
  bof.f_1234[3] = (*pf)[BO32_3];
  return(bof.f);
}

void sw_set_float_1234(pf, f)
float_1234 *pf;
float f;
{
  union {
    float_1234 f_1234;
    float f;
  } bof;
  bof.f = (float)f;
  (*pf)[BO32_0] = bof.f_1234[0];
  (*pf)[BO32_1] = bof.f_1234[1];
  (*pf)[BO32_2] = bof.f_1234[2];
  (*pf)[BO32_3] = bof.f_1234[3];
}

double sw_get_double_12345678(pd)
double_12345678 *pd;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d_12345678[0] = (*pd)[BO64_0];
  bod.d_12345678[1] = (*pd)[BO64_1];
  bod.d_12345678[2] = (*pd)[BO64_2];
  bod.d_12345678[3] = (*pd)[BO64_3];
  bod.d_12345678[4] = (*pd)[BO64_4];
  bod.d_12345678[5] = (*pd)[BO64_5];
  bod.d_12345678[6] = (*pd)[BO64_6];
  bod.d_12345678[7] = (*pd)[BO64_7];
  return(bod.d);
}

void sw_set_double_12345678(pd, d)
double_12345678 *pd;
double d;
{
  union {
    double_12345678 d_12345678;
    double d;
  } bod;
  bod.d = d;
  (*pd)[BO64_0] = bod.d_12345678[0];
  (*pd)[BO64_1] = bod.d_12345678[1];
  (*pd)[BO64_2] = bod.d_12345678[2];
  (*pd)[BO64_3] = bod.d_12345678[3];
  (*pd)[BO64_4] = bod.d_12345678[4];
  (*pd)[BO64_5] = bod.d_12345678[5];
  (*pd)[BO64_6] = bod.d_12345678[6];
  (*pd)[BO64_7] = bod.d_12345678[7];
}

如果不与访问器一起使用,这些类型定义的好处是会引发编译器错误,从而减少被遗忘的访问器错误。

typedef char int8_1[1], uint8_1[1];

typedef char int16_12[2], uint16_12[2]; /* little endian */
typedef char int16_21[2], uint16_21[2]; /* big endian */

typedef char int24_321[3], uint24_321[3]; /* Alpha Micro, PDP-11 */

typedef char int32_1234[4], uint32_1234[4]; /* little endian */
typedef char int32_3412[4], uint32_3412[4]; /* Alpha Micro, PDP-11 */
typedef char int32_4321[4], uint32_4321[4]; /* big endian */

typedef char int64_12345678[8], uint64_12345678[8]; /* little endian */
typedef char int64_34128756[8], uint64_34128756[8]; /* Alpha Micro, PDP-11 */
typedef char int64_87654321[8], uint64_87654321[8]; /* big endian */

typedef char float_1234[4]; /* little endian */
typedef char float_3412[4]; /* Alpha Micro, PDP-11 */
typedef char float_4321[4]; /* big endian */

typedef char double_12345678[8]; /* little endian */
typedef char double_78563412[8]; /* Alpha Micro? */
typedef char double_87654321[8]; /* big endian */
于 2016-05-23T08:47:44.067 回答
0

使用 ye olde 3-step-xor 技巧围绕模板函数中的枢轴进行字节交换提供了一种灵活、快速的 O(ln2) 解决方案,不需要库,这里的样式也拒绝 1 字节类型:

template<typename T>void swap(T &t){
    for(uint8_t pivot = 0; pivot < sizeof(t)/2; pivot ++){
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
        *((uint8_t *)&t+sizeof(t)-1- pivot) ^= *((uint8_t *)&t + pivot);
        *((uint8_t *)&t + pivot) ^= *((uint8_t *)&t+sizeof(t)-1- pivot);
    }
}
于 2019-04-11T14:18:39.897 回答
0

似乎安全的方法是在每个单词上使用 htons。所以,如果你有...

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
于 2019-04-19T03:40:25.687 回答
0

这是交换小端和大端的基本功能。它是基本的,但不需要补充库。

void endianness_swap(uint32_t& val) {
    uint8_t a, b, c;
    a = (val & 0xFF000000) >> 24;
    b = (val & 0x00FF0000) >> 16;
    c = (val & 0x0000FF00) >> 8;
    val=(val & 0x000000FF) << 24;
    val = val + (c << 16) + (b << 8) + (a);
}
于 2020-07-26T22:27:28.147 回答
0
void writeLittleEndianToBigEndian(void* ptrLittleEndian, void* ptrBigEndian , size_t bufLen )
{
    char *pchLittleEndian = (char*)ptrLittleEndian;

    char *pchBigEndian = (char*)ptrBigEndian;

    for ( size_t i = 0 ; i < bufLen ; i++ )    
        pchBigEndian[bufLen-1-i] = pchLittleEndian[i];
}

std::uint32_t row = 0x12345678;

char buf[4]; 

writeLittleEndianToBigEndian( &row, &buf, sizeof(row) );
于 2021-05-09T14:32:36.423 回答
0

不如使用内在函数高效,但肯定是可移植的。我的答案:

#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));
}
于 2021-05-25T02:34:05.473 回答
0

现在存在 std::endian 但在 c++23 添加 std::byteswap 之前的 c++20 无分支版本

#include <bit>
#include <type_traits>
#include <concepts>
#include <array>
#include <cstring>
#include <iostream>
#include <bitset>

template <int LEN, int OFF=LEN/2>
class do_swap
{
    // FOR 8 bytes:
    // LEN=8 (LEN/2==4)       <H><G><F><E><D><C><B><A>
    // OFF=4: FROM=0, TO=7 => [A]<G><F><E><D><C><B>[H]
    // OFF=3: FROM=1, TO=6 => [A][B]<F><E><D><C>[G][H]
    // OFF=2: FROM=2, TO=5 => [A][B][C]<E><D>[F][G][H]
    // OFF=1: FROM=3, TO=4 => [A][B][C][D][E][F][G][H]
    // OFF=0: FROM=4, TO=3 => DONE
public:
    enum consts {FROM=LEN/2-OFF, TO=(LEN-1)-FROM};
    using NXT=do_swap<LEN, OFF-1>;
// flip the first and last for the current iteration's range
    static void flip(std::array<std::byte, LEN>& b)
    {
        std::byte tmp=b[FROM];
        b[FROM]=b[TO];
        b[TO]=tmp;
        NXT::flip(b);
    }
};
template <int LEN>
class do_swap<LEN, 0> // STOP the template recursion
{
public:
    static void flip(std::array<std::byte, LEN>&)
    {
    }
};

template<std::integral T, std::endian TO, std::endian FROM=std::endian::native>
        requires ((TO==std::endian::big) || (TO==std::endian::little))
              && ((FROM==std::endian::big) || (FROM==std::endian::little))
class endian_swap
{
public:
    enum consts {BYTE_COUNT=sizeof(T)};
    static T cvt(const T integral)
    {
    // if FROM and TO are the same -- nothing to do
        if (TO==FROM)
        {
                return integral;
        }

    // endian::big --> endian::little is the same as endian::little --> endian::big
    // the bytes have to be reversed
    // memcpy seems to be the most supported way to do byte swaps in a defined way
        std::array<std::byte, BYTE_COUNT> bytes;
        std::memcpy(&bytes, &integral, BYTE_COUNT);
        do_swap<BYTE_COUNT>::flip(bytes);
        T ret;
        std::memcpy(&ret, &bytes, BYTE_COUNT);
        return ret;
    }
};

std::endian big()
{
    return std::endian::big;
}

std::endian little()
{
    return std::endian::little;
}

std::endian native()
{
    return std::endian::native;
}

long long swap_to_big(long long x)
{
    return endian_swap<long long, std::endian::big>::cvt(x);
}

long long swap_to_little(long long x)
{
    return endian_swap<long long, std::endian::little>::cvt(x);
}

void show(std::string label, long long x)
{
    std::cout << label << "\t: " << std::bitset<64>(x) << " (" << x << ")" << std::endl;
}

int main(int argv, char ** argc)
{
    long long init=0xF8FCFEFF7F3F1F0;
    long long to_big=swap_to_big(init);
    long long to_little=swap_to_little(init);
    show("Init", init);
    show(">big", to_big);
    show(">little", to_little);
}
于 2021-10-08T19:11:44.523 回答
-1

以下是如何读取以 IEEE 754 64 位格式存储的双精度数据,即使您的主机使用不同的系统也是如此。

/*
* read a double from a stream in ieee754 format regardless of host
*  encoding.
*  fp - the stream
*  bigendian - set to if big bytes first, clear for little bytes
*              first
*
*/
double freadieee754(FILE *fp, int bigendian)
{
    unsigned char buff[8];
    int i;
    double fnorm = 0.0;
    unsigned char temp;
    int sign;
    int exponent;
    double bitval;
    int maski, mask;
    int expbits = 11;
    int significandbits = 52;
    int shift;
    double answer;

    /* read the data */
    for (i = 0; i < 8; i++)
        buff[i] = fgetc(fp);
    /* just reverse if not big-endian*/
    if (!bigendian)
    {
        for (i = 0; i < 4; i++)
        {
            temp = buff[i];
            buff[i] = buff[8 - i - 1];
            buff[8 - i - 1] = temp;
        }
    }
    sign = buff[0] & 0x80 ? -1 : 1;
    /* exponet in raw format*/
    exponent = ((buff[0] & 0x7F) << 4) | ((buff[1] & 0xF0) >> 4);

    /* read inthe mantissa. Top bit is 0.5, the successive bits half*/
    bitval = 0.5;
    maski = 1;
    mask = 0x08;
    for (i = 0; i < significandbits; i++)
    {
        if (buff[maski] & mask)
            fnorm += bitval;

        bitval /= 2.0;
        mask >>= 1;
        if (mask == 0)
        {
            mask = 0x80;
            maski++;
        }
    }
    /* handle zero specially */
    if (exponent == 0 && fnorm == 0)
        return 0.0;

    shift = exponent - ((1 << (expbits - 1)) - 1); /* exponent = shift + bias */
    /* nans have exp 1024 and non-zero mantissa */
    if (shift == 1024 && fnorm != 0)
        return sqrt(-1.0);
    /*infinity*/
    if (shift == 1024 && fnorm == 0)
    {

#ifdef INFINITY
        return sign == 1 ? INFINITY : -INFINITY;
#endif
        return  (sign * 1.0) / 0.0;
    }
    if (shift > -1023)
    {
        answer = ldexp(fnorm + 1.0, shift);
        return answer * sign;
    }
    else
    {
        /* denormalised numbers */
        if (fnorm == 0.0)
            return 0.0;
        shift = -1022;
        while (fnorm < 1.0)
        {
            fnorm *= 2;
            shift--;
        }
        answer = ldexp(fnorm, shift);
        return answer * sign;
    }
}

对于其余的函数套件,包括写入和整数例程,请参阅我的 github 项目

https://github.com/MalcolmMcLean/ieee754

于 2017-05-22T22:40:33.593 回答
-4

查找位移,因为这基本上是从 little -> big endian 交换所需要做的一切。然后根据位大小,您可以更改进行位移的方式。

于 2008-09-19T20:30:37.673 回答