Little endian & Big endian

博客介绍了big-endian和little-endian两种不同的字节序,指出在跨平台通信和资源共享时需考虑字节序问题。还给出不同字节序转换的方法及代码,包括通用转换代码、测试字节序代码,同时提及float类型处理及读写指定字节序文件的代码,最后列出常见处理器和操作系统的字节序情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个标题中的Endian是什么意思呢?还是让我们先来看看下面的情况,这是内存中一个WORD值中的内容,那么这个WORD中的值是0x1234呢,还是0x3412 ? 

low byte high byte 
0x12 0x34 

熟悉x86汇编的人立刻就知道这个值应为0x3412,很对,但在一些情况下,比如说你在SGI的机器上看到这种情况,则正好相反,0x1234才是正确答案,这与CPU内部处理数据的方式有关。这两种处理方式都存在于不同厂商生产的CPU之中,在上例中若此WORD值为0x3412的,我们称之为little-endian, 若为0x1234的,我们称之为big-endian,这是两种不同的byte orders。MSDN中有比较精确的定义如下:

Byte Ordering Byte ordering Meaning 
big-endian The most significant byte is on the left end of a word. 
little-endian The most significant byte is on the right end of a word. 

一般来说我们不用关心byte ordering的问题,但若要涉及跨平台之间的通信和资源共享,则不得不考虑这个问题了。也许你会说,我永远不会去用其它非x86的CPU,也许是这样,你甚至可以不必知道我们最常用的Intel,AMD等生产的x86的byte ordering是little-endian的,而且按现在的装机数量来看,可以说世界上绝大多数CPU是little-endian的,但多了解一些没有什么坏处,也许有用上的一天,实际若您要涉及到网络编程,了解一些还是有所帮助的,看完本文后您就应该知道为何socket编程中为何要用到如 ntohl, htonl, ntohs, htons这几个看起来名字似乎怪怪的API了,也很容易理解这些函数名的意义了。

假设我们要在不同byte ordering的机器之间传输和交换数据,那该怎么办呢,有两个方法,一是全部转换成文本来传输(如XML使用的),另一个方法两方都按照某一方的byte order,这时就涉及到了不同byte order之间相互转换的问题(网络传输标准如TCP/IP采用第二种方法并且由于历史的原因,byte ordering是big-endian的)。两种之间该如何转换呢?方法有很多,我们可以先看看MFC中在处理serialize的代码中所用的方法(List), 虽然代码应该是高效易读的, 但我个人并不喜欢它, 原因是我觉得这不是一种通用优美的方法.下面列出的是我自己写的转换的代码:


template
F3D_INLINE T ConvertEndian(T  t)
{
T tResult = 0;
for (int  I = 0; I < sizeof(T); ++ I)
{
tResult <<= 8;
tResult |= (t & 0xFF) ;

t >>= 8;
}

return  tResult;
}

原理非常简单,交换字节顺序,我就不多说了,当然这个写法并不是快速的, 只是通用的(我没条件试, 若有不对之处请指出), 若要快速的代码,可以在不同platform上用与platform相关的代码, 如在PowerPC上有 "load word byte-reversed indexed" (lwbrx) 和 "load halfword byte-reversed indexed" (lhbrx) 指令, 在x86上还可用BSWAP单个汇编指令等,在类型上专为int16, int32写的通用的代码也可以比这快得多. 

当然如果在byte ordering相同的情况下,应该不必用这个转换函数,所以我们可以定义一个宏来处理不同的byte ordering,也可以在运行时测试byte ordering, 下面的代码给出了一个简单的测试方法。 


// Test for endianness.
F3D_INLINE bool IsLittleEndian(void)
{
DWORD dwTestValue = 0x12345678L;
return  (*((BYTE*)&dwTestValue) == 0x78);
}

但是float比较怪,有可能所涉及到不仅仅是byte order的问题,因为有些平台如Alpha不使用IEEE的浮点格式,还得自己转换。当然同上,其它的方法一是将所用的float用文本方式输入输出,另一个办法是在某些情况下可将其转换成定点数再处理,这里我不再深入。

如果是读写第三方已经指定byte order的文件或数据流,比如说读SGI的位图文件格式,则可以直接自行按指定的byte order拼起来,不必考虑host机是何种byte ordering。下面我给出相应的代码:


// Read a little-endian TYPE from address
template
F3D_INLINE T GetLittleEndian(const BYTE*  pBuf)
{
T tResult = 0;
pBuf += sizeof(T) - 1;
for (int  I = 0; I < sizeof(T); ++ I)
{
tResult <<= 8;
tResult |= *pBuf --;
}

return  tResult;
}

// Read a big-endian TYPE from address
template
F3D_INLINE T GetBigEndian(const BYTE*  pBuf)
{
T tResult = 0;
for (int  I = 0; I < sizeof(T); ++ I)
{
tResult <<= 8;
tResult |= *pBuf ++;
}

return  tResult;
}

// Set a little-endian TYPE on a address
template
F3D_INLINE void SetLittleEndian(BYTE*  pBuf, T  t)
{
for (int  I = 0; I < sizeof(T); ++ I)
{
*pBuf ++ = BYTE(t & 0xFF);
t >>= 8;
}
}

// Set a big-endian T on a address
template
F3D_INLINE void SetBigEndian(BYTE*  pBuf, T  t)
{
pBuf += sizeof(T) - 1;
for (int  I = 0; I < sizeof(T); ++ I)
{
*pBuf -- = BYTE(t & 0xFF);
t >>= 8;
}
}

从上文可以看出,byte order挺简单的,一般应用中可能也用不上,但若您对写跨平台的程序有兴趣,则一定要了解的比较清楚才行。以上代码都是从实际使用的源码中取下来的。 

附:常见Processor, OS的byte ordering情况

Processor OS Order 
x86 (Intel, AMD, … ) All little-endian 
DEC Alpha All little-endian 
HP-PA NT little-endian 
HP-PA UNIX big-endian 
SUN SPARC All? big-endian 
MIPS NT little-endian 
MIPS UNIX big-endian 
PowerPC NT little-endian 
PowerPC non-NT big-endian 
RS/6000 UNIX big-endian 
Motorola m68k All big-endian 



下文转至优快云  李新刚的 blog




    // =============================================
    // Class for 16 bit numbers.
    // =============================================
    template <class T>
    class CBe16
    {
        typedef union 
        {
            T i;
            struct 
            {
                char a,b;
            } c;
        } Union16;
    
        char a,b;
    
    public:
        // Sets "big-endian" value.
        void operator ()(T nValue)
        {
            Union16 u16;
            u16.i = nValue;
    
            a = u16.c.b;
            b = u16.c.a;
        }
    
        // Returns "little-endian" value.
        T operator ()()
        {
            Union16 u16;
    
            u16.c.b = a;
            u16.c.a = b;
    
            return u16.i;
        }
    };
    
    typedef CBe16<short> CBeShort;
    typedef CBe16<unsigned short> CBeUShort;
    
    // =============================================
    // Class for 32 bit numbers.
    // =============================================
    template <class T>
    class CBe32
    {
        typedef union 
        {
            T i;
            struct 
            {
                char a,b,c,d;
            } c;
        } Union32;
    
        char a,b,c,d;
    
    public:
        // Sets "big-endian" value.
        void operator ()(T nValue)
        {
            Union32 u32;
            u32.i = nValue;
    
            a = u32.c.d;
            b = u32.c.c;
            c = u32.c.b;
            d = u32.c.a;
        }
    
        // Returns "little-endian" value.
        T operator ()()
        {
            Union32 u32;
    
            u32.c.d = a;
            u32.c.c = b;
            u32.c.b = c;
            u32.c.a = d;
    
            return u32.i;
        }
    };
    
    typedef CBe32<int> CBeInt;
    typedef CBe32<unsigned int> CBeUInt;
    typedef CBe32<long> CBeLong;
    typedef CBe32<unsigned long> CBeULong;
    typedef CBe32<float> CBeFloat;
    
    // =============================================
    // Class for 64 bit numbers.
    // =============================================
    class CBeDouble
    {
        typedef union 
        {
            double d;
            struct 
            {
                char a,b,c,d,e,f,g,h;
            } c;
        } Union64;
    
        char a,b,c,d,e,f,g,h;
    
    public:
        // Sets "big-endian" value.
        void operator ()(double dValue)
        {
            Union64 u64;
            u64.d = dValue;
    
            a = u64.c.h;
            b = u64.c.g;
            c = u64.c.f;
            d = u64.c.e;
            e = u64.c.d;
            f = u64.c.c;
            g = u64.c.b;
            h = u64.c.a;
        }
    
        // Returns "little-endian" value.
        double operator ()()
        {
            Union64 u64;
    
            u64.c.h = a;
            u64.c.g = b;
            u64.c.f = c;
            u64.c.e = d;
            u64.c.d = e;
            u64.c.c = f;
            u64.c.b = g;
            u64.c.a = h;
    
            return u64.d;
        }
    };
    
    typedef unsigned short     uint16;
    typedef unsigned long      uint32;
    
    //-------------------------------------
    // Basic swaps
    //-------------------------------------
    template <typename T>
    inline T WordSwapC( T w )
    {
       uint16 temp;
    
       temp  = ((*((uint16 *)&w) & 0xff00) >> 8);
       temp |= ((*((uint16 *)&w) & 0x00ff) << 8);
    
       return *((T*)&temp);
    }
    
    template <typename T>
    inline T DWordSwapC( T dw )
    {
       uint32 temp;
    
       temp  =   *((uint32 *)&dw)               >> 24;
       temp |= ((*((uint32 *)&dw) & 0x00FF0000) >> 8);
       temp |= ((*((uint32 *)&dw) & 0x0000FF00) << 8);
       temp |= ((*((uint32 *)&dw) & 0x000000FF) << 24);
    
       return *((T*)&temp);
    }
    
    //-------------------------------------
    // Fast swaps
    //-------------------------------------
    template <typename T>
    inline T WordSwapAsm( T w )
    {
       __asm
       {
          mov ax, w
          xchg al, ah
       }
    }
    
    template <typename T>
    inline T DWordSwapAsm( T dw )
    {
       __asm
       {
          mov eax, dw
          bswap eax
       }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值