首页 文章

如何在C中的big-endian和little-endian值之间进行转换?

提问于
浏览
168

如何在C中的big-endian和little-endian值之间进行转换?

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

编辑#2:我接受的答案直接适用于我正在编制的编译器(这就是我选择它的原因) . 但是,这里有其他非常好的,更便携的答案 .

28 回答

  • 12

    我以为我在这里添加了自己的解决方案,因为我没有在任何地方看到它 . 它是一个小巧便携的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;
    }
    
  • 4

    如果您正在使用 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位数字(字符) .

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

    对于浮点数和双精度数,它与普通整数一样困难,因为这些可能与否可能在主机的字节顺序中 . 您可以在big-endian机器上获得little-endian浮点数,反之亦然 .

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

    例如,在 GCC 中您可以直接调用:

    int32_t __builtin_bswap32 (int32_t x)
    int64_t __builtin_bswap64 (int64_t x)
    

    (不需要包含某些内容) . Afaik bits.h也以非gcc为中心的方式声明了相同的功能 .

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

    调用内在函数而不是自己编辑可以获得最佳性能和代码密度 .

  • 6

    简单的说:

    #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) .

  • 61

    来自Rob Pyke的 The Byte Order Fallacy

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

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

    如果它是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'已明确定义 .

    注意:注释中没有显式类型转换,重要的是 dataunsigned charuint8_t 的数组 . 使用 signed charchar (如果已签名)将导致 data[x] 被提升为整数,并且 data[x] << 24 可能将1移入符号位(UB) .

  • 15

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

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

  • 144

    我从这篇文章中提出了一些建议并将它们组合在一起形成:

    #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);
    }
    
  • 2

    有一个名为BSWAP的汇编指令,可以非常快速地为您进行交换 . 你可以阅读它here .

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

  • 0

    从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);
    }
    
  • 2

    我们用模板完成了这个 . 你可以这样:

    // 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);
    }
    
  • 2

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

  • 5

    与C中的方式相同:

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

    您还可以声明一个无符号字符向量,将输入值memcpy到其中,将字节转换为另一个向量并将字节记忆输出,但这将比使用64位值的位数长几个数量级 .

  • 0

    在大多数POSIX系统上(通过它不在POSIX标准中)有endian.h,它可用于确定系统使用的编码 . 从那里它是这样的:

    unsigned int change_endian(unsinged int x)
    {
        unsigned char *ptr = (unsigned char *)&x;
        return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
    }
    

    这会交换顺序(从big-endian到little endian):

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

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

  • 0

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

    #define htonl(x) _byteswap_ulong(x)
    

    这对于.PNG图像处理尤其重要,其中所有整数都保存在Big Endian中,并解释“一个人可以使用htonl()...”{以减慢典型的Windows程序,如果你没有准备的话} .

  • 0

    大多数平台都有一个系统头文件,可以提供有效的字节交换功能 . 在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
    
  • 70

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

    long swap(long i) {
        char *c = (char *) &i;
        return * (long *) (char[]) {c[3], c[2], c[1], c[0] };
    }
    
  • -4

    说真的......我不明白为什么所有的解决方案都是 complicatedHow about the simplest, most general template function that swaps any type of any size under any circumstances in any operating system????

    template <typename T>
    void SwapEnd(T& var)
    {
        char* varArray = reinterpret_cast<char*>(&var);
        for(long i = 0; i < static_cast<long>(sizeof(var)/2); i++)
            std::swap(varArray[sizeof(var) - 1 - i],varArray[i]);
    }
    

    这是C和C的神奇力量!只需将原始变量字符交换为字符 .

    请记住,我没有使用简单赋值运算符“=”,因为当翻转字节序并且复制构造函数(或赋值运算符)不起作用时,某些对象将被搞砸 . 因此,通过char复制它们更可靠 .

    要打电话,只需使用

    double x = 5;
    SwapEnd(x);
    

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

  • 11

    我有这个代码,允许我从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);
    }
    
  • 3

    如果big-endian 32位无符号整数看起来像0xAABBCCDD等于2864434397,那么相同的32位无符号整数在小端处理器上看起来像0xDDCCBBAA,它也等于2864434397 .

    如果big-endian 16位无符号短路看起来像0xAABB等于43707,则相同的16位无符号短路看起来像小端处理器上的0xBBAA,也等于43707 .

    这里有几个方便的#define函数可以将字节从little-endian交换到big-endian,反之亦然 - >

    // 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))
    
  • 3

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

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

    Disclaimer: 我还没有尝试编译或测试它 .

  • 4

    如果你采用公共模式来反转一个字中的位的顺序,并且剔除在每个字节内反转位的部分,那么你只剩下一些只能反转字内字节的东西 . 对于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);
    
  • 1

    使用下面给出的代码,您可以轻松地在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))
    
  • 0

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

  • 48

    哇,我简直不敢相信我在这里读过的一些答案 . 实际上,汇编中的指令比其他任何东西都更快 . BSWAP . 你可以简单地写一个像这样的函数......

    __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 bl, bh
    xchg al, ah
    shl eax, 16
    or eax, ebx
    

    内联汇编仅适用于Visual Studio中的x86代码 . 裸函数不能排列,也不能在x64版本中使用 . 那个例子,你将不得不使用编译器内在函数 .

  • 4

    用于实现优化器友好的未对齐非现场端访问器的便携式技术 . 它们适用于每个编译器,每个边界对齐和每个字节排序 . 这些未对齐的例程根据本机字节序和对齐方式进行补充或提出 . 部分列表,但你明白了 . 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具有引发编译器错误的好处,从而减轻了被遗忘的访问器错误 .

    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 */
    
  • 8

    我最近在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) 块中 . 这是一个common idiom,允许在宏之后使用正常的分号 .

    接下来是使用名为 REVERSE_BYTES 的变量作为 for 循环's counter. The name of the macro itself is used as a variable name to ensure that it doesn' t与使用宏的任何其他符号进行冲突 . 由于该名称正在宏_572374中使用,因此在此处用作变量名时将再次展开 .

    for 循环中,有两个字节被引用,XOR swapped(因此不需要临时变量名):

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

    VA_ARGS表示给宏的任何内容,并用于增加可传递的内容的灵活性(尽管不是很多) . 然后获取该参数的地址并转换为 unsigned char 指针,以允许通过数组 [] 下标交换其字节 .

    最后一个特点是缺乏 {} 大括号 . 它们不是必需的,因为每个交换中的所有步骤都与comma operator连接,使它们成为一个语句 .

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

  • 6

    试试 Boost::endian ,不要自己实施!

    这是link

  • 25

    以下是如何读取以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;
        }
    }
    

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

    https://github.com/MalcolmMcLean/ieee754

  • 1

    查找位移,因为这基本上是你需要做的从小 - >大端交换 . 然后,根据位大小,您可以更改位移的方式 .

相关问题