当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C++中的big-endian和little-endian值之间进行转换?》经验,为你挑选了12个好方法。

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

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

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



1> Nils Pipenbr..:

如果您使用的是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位交换它只是一个位旋转.

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


值得注意的是,这些内在函数/总是/交换字节,它们不像`htonl`,`htons`等.你必须从你的情况的上下文知道何时实际交换字节.
使用GCC,我可能会使用:#include int32_t bswap_32(int32_t x)int64_t bswap_64(int64_t x)
@Jason因为大小端的8位数是相同的.:-)
`__builtin_bswapX`只能从GCC-4.3开始提供
@BrianVandenberg对; 使用`htonl`和`ntohl`而不用担心上下文在编写可移植代码时会起作用,因为定义这些函数的平台会交换它,如果它是小/中端,而在big-endian上则是无操作.但是,当解码定义为little-endian(比如BMP)的标准文件类型时,仍然必须知道上下文并且不能仅仅依赖于`htonl`和`ntohl`.

2> Alexandre C...:

简单的说:

#include 

template 
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(42).


@Rapptz:3.10似乎很清楚:"如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[...]**char或unsigned char类型.**".也许我在这里遗漏了一些东西,但我很清楚通过char指针访问任何类型都是明确允许的.
有一个upvote.我刚刚使用了uchars,并分配了4到1,3或2,2到3和1到4,但如果你有不同的尺寸,这会更灵活.第一代Pentium IIRC上有6个时钟.BSWAP是1个时钟,但是特定于平台.
@MihaiTodor:标准明确允许通过一系列字符使用联合进行类型转换.见例如.[这个问题](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule).
@AlexandreC.不是在C++标准中 - 仅在C中.在C++中(此代码是),此代码是未定义的行为.
@RocketRoy:是的,如果速度成为一个问题,那么用平台和类型特定的内在函数编写重载非常简单.

3> Matthieu M...:

来自Rob Pyke 的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:不要担心你的平台本机顺序,所有重要的是你正在阅读的流的字节顺序,你最好希望它的定义很好.

注意:在评论中注释到没有显式类型转换,重要的是data成为unsigned char或者数组uint8_t.使用signed charchar(如果有符号)将导致data[x]被提升为整数并data[x] << 24可能将1移入符号位UB.


这很酷,但在我看来它只适用于整数和变体.如何处理花车/双打?
如果`i`签名,这会有用吗?
在一个松散相关的说明中,链接的帖子是一些不愉快的阅读...这家伙似乎很重视简洁,但他更喜欢写一个关于所有那些不像开头直播那样开明的坏程序员的长吼,而不是实际解释情况以及为什么他的解决方案始终有效.
@meowsqueak:是的,我希望它能工作,因为只有字节顺序改变,而不是每个字节内的位顺序.

4> Frosty..:

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

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


要避开不可避免的问题:BE平台需要LE的原因有很多; 许多文件格式(BMP,FLI,PCX,QTM,RTF,TGA仅举几例)用小尾数值......至少,格式的一些版本在同一时间做呢.
网络字节排序是大端,我相信.即使您没有使用网络代码,也可以使用这些功能.但是没有浮动版本ntohf或htonf
马特H.这只是大多数都是正确的.并非所有计算机系统都具有little-endian字节顺序.如果您正在研究,比如说motorolla 68k,PowerPC或其他大端架构,这些函数根本不会交换字节,因为它们已经处于"网络字节顺序".
不幸的是,`htonl`和`ntohl`不能在big-endian平台上使用小端.
@celtschk,明白了; 然而,即使在大端环境中,OP也想要一种切换字节序的方法.

5> Steve Lorime..:

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

#include 
#include 
#include 
#include 

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
struct swap_bytes
{
    inline T operator()(T val)
    {
        throw std::out_of_range("data size");
    }
};

template
struct swap_bytes
{
    inline T operator()(T val)
    {
        return val;
    }
};

template
struct swap_bytes
{
    inline T operator()(T val)
    {
        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
    }
};

template
struct swap_bytes
{
    inline T operator()(T val)
    {
        return ((((val) & 0xff000000) >> 24) |
                (((val) & 0x00ff0000) >>  8) |
                (((val) & 0x0000ff00) <<  8) |
                (((val) & 0x000000ff) << 24));
    }
};

template<>
struct swap_bytes
{
    inline float operator()(float val)
    {
        uint32_t mem =swap_bytes()(*(uint32_t*)&val);
        return *(float*)&mem;
    }
};

template
struct swap_bytes
{
    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
{
    inline double operator()(double val)
    {
        uint64_t mem =swap_bytes()(*(uint64_t*)&val);
        return *(double*)&mem;
    }
};

template
struct do_byte_swap
{
    inline T operator()(T value)
    {
        return swap_bytes()(value);
    }
};
// specialisations when attempting to swap to the same endianess
template struct do_byte_swap { inline T operator()(T value) { return value; } };
template struct do_byte_swap { inline T operator()(T value) { return value; } };

} // namespace detail

template
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::value);

    return detail::do_byte_swap()(value);
}



6> Kevin..:

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


我不认为使用逻辑 - 和(&&)而不是按位和(&)是正确的.根据C++规范,两个操作数都被隐式转换为bool,这不是你想要的.
这里发布的最后一个函数是不正确的,应编辑为:void swapByteOrder(unsigned long long&ull){ull =(ull >> 56)| ......(ull << 56); }

7> anon6439..:

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

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



8> Mark..:

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

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



9> Andrew..:

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



10> Ben Straub..:

和你在C中做的一样:

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

您还可以声明一个无符号字符向量,将输入值memcpy到其中,将字节转换为另一个向量并将字节记忆输出,但这将比bit-twiddling长几个数量级,特别是对于64位值.



11> terminus..:

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

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

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

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


这很有趣 - http://www.opengroup.org/onlinepubs/9699919799/toc.htm上的POSIX标准没有提到标题'`.

12> user2699548..:

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

#define htonl(x) _byteswap_ulong(x)

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

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有