Chinaunix首页 | 论坛 | 博客
  • 博客访问: 295195
  • 博文数量: 44
  • 博客积分: 2276
  • 博客等级: 大尉
  • 技术积分: 439
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-01 09:07
文章分类

全部博文(44)

文章存档

2010年(1)

2009年(1)

2008年(30)

2007年(12)

分类: LINUX

2007-11-25 13:53:35

二进制文件的字节顺序问题:
大端字节(big-endian)和小端字节(little-endian)

    今天碰一个关于字节顺序的问题,虽然看起来很简单,但一直都没怎么完全明白这个东西,索性就找了下资料,把它弄清楚.
    因为现行的计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big-endian和litte-endian.
    考虑一个short整数0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可能有如下两种情况:
大端字节(Big-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
       0x1000                  0x1001
_____________________________
|                           |
|         0x31             |       0x32
|________________ | ________________
高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)
 
小端字节(little-endian):
----------------->>>>>>>>内存地址增大方向
short变量地址
       0x1000                  0x1001
_____________________________
|                           |
|         0x32             |       0x31
|________________ | ________________
低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)
 
可以做个实验
在windows上下如下程序
#include
#include ^&*,见鬼去吧 -_-|||
     为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。
     无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。
这里有一段W. Richard Stevens的代码,用于判断字节序
/*
* gcc -Wall -pipe -O3 -s -o byte_order byte_order.c
*/
#include
#include
/*
* return value:
* 1 big-endian
* 2 little-endian
* 3 unknow
* 4 sizeof( unsigned short int ) != 2
*/
static int byte_order ( void )
{
union
{
unsigned short int s;
unsigned char c[ sizeof( unsigned short int ) ];
} un;
un.s = 0x0201;
if ( 2 == sizeof( unsigned short int ) )
{
if ( ( 2 == un.c[0] ) && ( 1 == un.c[1] ) )
{
puts( "big-endian" );
return( 1 );
}
else if ( ( 1 == un.c[0] ) && ( 2 == un.c[1] ) )
{
puts( "little-endian" );
return( 2 );
}
else
{
puts( "unknow" );
return( 3 );
}
}
else
{
printf( "sizeof( unsigned short int ) = %u\n", ( unsigned int )sizeof(unsigned short int ) );
return( 4 );
}
return( 3 );
} /* end of byte_order */
int main ( int argc, char * argv[] )
{
printf( "byte_order() = %d\n", byte_order() );
return( EXIT_SUCCESS );
} /* end of main */


小端方式每个字的低位字节在低地址,而大端方式每个字的低位字节在高地址,因此小端存储顺序是正常的,大端存储顺序是相反的。

但是在调试器中,如果按照地址递增的方式看过去,小端格式的内容是非常别扭的,而大端格式是正常的顺序。

例如0x12345678小端方式存放如下:
地址                      内容
A                          78
A+1                        56
A+2                        34
A+3                        12


大端方式存放如下:
地址                      内容
A                          12
A+1                        34
A+2                        56
A+3                        78

大端和小端字节序的问题在网络中以及在不同的操作系统的兼容性中是一个比较大的问题。它关系到不同操作系统和网络传输是否能够保证数据的语义正确性。
    对于一个字节而言,大端和小端没有任何的区别,但是对于多个字节而言,就存在着显著的区别。这个区别我们可以很容易想到,如果提供了一个地址,比如0x37041200,需要读取这个地址的一个字,也就是4个字节的一个数据。那么是读取从0x37041200开始到0x37041300这样的一个数,还是读取从0x37041200开始到0x37041100存储的4个字节的数。为此就出现了不同公司的两种实现--一个就是大端,一个就是小端。


----------------------------------------------------------
假设:a=0x12345678;

则大端字节序和小端字节序的存储如下图所示:
                 Big-Endian                               Little-Endian
0字节            12h                                       78h
1字节            34h                                       56h
2字节            56h                                       34h
3字节            78h                                       12h
h表示16进制

小端:低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位在前->与正常逻辑顺序相反)

你也可以用下面的程序测验你的机器是大端字节序还是小端字节序:
----------------------------------------------------------
#include
int IsLittleEndian()
{
unsigned int usData = 0x12345678;
unsigned char *pucData = (unsigned char*)&usData;
if(*pucData == 0x78)
     return 1;
else
     return 0;
}

int main(void)
{
    if(IsLittleEndian())
        printf("is little endian!\n");
    else
        printf("is big endian!\n");
    return 0;
}

我的机器是Intel的X86系列,所以是little endian.

----------------------------------------------------------
a=0x12345678

The Basics

"Little Endian" means that the low-order byte of the number is stored in memory at the lowest address, and the high-order byte at the highest address. (The little end comes first.) For example, a 4 byte LongInt

    Byte3 Byte2 Byte1 Byte0

will be arranged in memory as follows:
    Base Address+0   Byte0     78h   
    Base Address+1   Byte1     56h
    Base Address+2   Byte2     34h
    Base Address+3   Byte3     12h

Intel processors (those used in PC's) use "Little Endian" byte order.


"Big Endian" means that the high-order byte of the number is stored in memory at the lowest address, and the low-order byte at the highest address. (The big end comes first.) Our LongInt, would then be stored as:

    Base Address+0   Byte3        12h
    Base Address+1   Byte2      34h
    Base Address+2   Byte1      56h
    Base Address+3   Byte0      78h

Motorola processors (those used in Mac's) use "Big Endian" byte order

Little Endian    Base Address(BA)指向数据的低位


              ------->    

            |--|--|--|--|
            |78|56|34|12|
            |--|--|--|--|
            BA   

            |--|--|--|--|
            |12|34|56|78|
            |--|--|--|--|
            BA

阅读(2475) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~