http://www.blogjava.net/Files/jinfeng_wang/Endian.rar
Endian 介绍
1. Endian简介
Endian可以看作是系统的一种属性, 它指示多字节整数是从左向右放, 还是从右向左放. 它有两种形式:
Big Endian
Little Endian
BE把多字节整数的MSB(Most Significant Byte)存储在最低的地址上, 把LSB(Least Significant Byte)顺序存放在最高的地址上, 而LE正好相反.
如4-bytes数据0x01020304以两种不同的方式存储如下:
00000001 00000010 00000011 00000100
Address
|
00
|
01
|
02
|
03
|
Big-Endian
|
00000001
|
00000010
|
00000011
|
00000100
|
Little-Endian
|
00000100
|
00000011
|
00000010
|
00000001
|
所有的处理器必须指定它用Big Endian还是Little Endian. Intel's 80x86 processor
是 little endian. Sun's SPARC, Motorola's 68K, 和PowerPC系列是big endian. 有些处理器甚至设置了一个标志位可以选择所需要的Endian.
2. 出现的问题
如果我们不了解Endian在数据存储上的差异, 使用的时候就有可能出现问题, 比如我们想要的是0x01020304,但是在little endian的情况下, 就有可能得到0x04030201.
如何避免错误的数据呢? 首先先看一下系统是如何存取数据的.
下面是同一组数据在两种endian下的memory dump:
char c1 = 1; char c2 = 2; short s = 255; // 0x00ff long l = 0x11223344;
|
Offset : Memory dump 0x0000 : 01 02 00 FF 0x0004 : 11 22 33 44
|
char c1 = 1; char c2 = 2; short s = 255; // 0x00ff long l = 0x11223344;
|
Offset : Memory dump 0x0000 : 01 02 FF 00 0x0004 : 44 33 22 11
|
A Little-Endian memory dump
|
上图表示了数据的存放方式, 虽然s, l在两种endina下的存储方式不同, 但取出s, l的时候系统还是会还原成原来的值, 数据是不会改变的. 就是说平常使用的过程中, 我们不必关心这种存取的过程.
那么在什么情况下会造成数值的改变呢? 我们从存数据和取数据两个方面进行说明
2.1. 存数据
有一些接口和规范规定了必须以某种Endian的格式进行通讯, 大多数都规定以Big Endian的格式. 比如SCSI command数据的传输, TCP/IP网络协议等.
下面是10 字节的Read command, 其CDB格式如下:
Bit
Byte
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0
|
Operation Code (28h)
|
1
|
Reserved
( 0 0 0 )b
|
DPO
|
FUA
( 0 )b
|
Reserved
( 0 0 0 )b
|
RelAdr ( 0 )b
|
2
|
(MSB)
LBA
(LSB)
|
3
|
4
|
5
|
6
|
Reserved ( 00 h)
|
7
|
(MSB)
Transfer Length (LSB)
|
8
|
9
|
Controller
|
我们定义如下的结构体填充.
typedef struct cdb1tag
{
uchar_t opcode;
uchar_t lun;
uint_t lba;
uchar_t rsv1;
ushort_t block;
uchar_t cntl;
} CDB1;
假设要发行一个Read操作, LBA是0x01020304. Transfer Length是255(0x00ff).
我们需要对CDB进行填充. 赋值:
lba=0x01020304; block=0x00ff; …(其他参数不讨论)
下面我们看看赋值后Big Endian和Little Endian是怎样存储这段数据的:
Byte
|
(BIG_ENDIAN)
|
0
|
|
1
|
|
2
|
(MSB) 01
|
3
|
02
|
4
|
03
|
5
|
04 (LSB)
|
6
|
|
7
|
(MSB) 00
|
8
|
ff (LSB)
|
9
|
|
Byte
|
(LITTLE_ENDIAN)
|
0
|
|
1
|
|
2
|
(LSB) 04
|
3
|
03
|
4
|
02
|
5
|
01 (MSB)
|
6
|
|
7
|
(LSB) ff
|
8
|
00 (MSB)
|
9
|
|
2.2. 取数据
同上面讲到的, 如果规定了必须以某种Endian通讯, 则必须按照规定的顺序把数据取出来, 这种情况同存数据的例子, 只不过一个是存, 一个是取.
union{
char c_num[4]; // 01 02 03 04 int i_num;
}dev;
|
Offset : Memory dump
0x0000 : 01 02 03 04
|
i_num= 0x04030201 (LE) : 0x01020304 (BE)
|
另外, 不按照系统存储的方式取数据, 有时会发生意外. 比如说我们是以一个字节一个字节存储的数据, 却以4个字节为一组取出.
不过有的数据比较特殊, 即使颠倒字节顺序, 数值也不发生改变
例如: 0 0x1111 0x01020201 …
3. 解决方法
多字节数据以单字节写入/读出
IN:
(unsigned char)((lba & 0Xff000000) >> 24) à MSB
(unsigned char)((lba & 0X00ff0000) >> 16)
(unsigned char)((lba & 0X0000ff00) >> 8)
(unsigned char) (lba & 0X000000ff) à LSB
OUT:
endian_dump()
交换字节 byte_swap()