大端 小端

http://blog.sina.com.cn/s/blog_6ffd3b5c0100mc4z.html

1. 小端

在bitstream的定义中,经常见到这样的定义

exam{
A:18 bit stream left bit first
B: 14 bit stream left bit first
}

在小端(little endian)系统中,上述32bit被读入内存,memory map如下
------------------------------------------------------------------------------------
| A7 ... A0 | A15 ... A8 | B5 ... B0 A17 A16 | B13 ... B6 |
    byte0        byte1            byte2           byte3

low address --------------------------------> high address
-------------------------------------------------------------------------------------
NOTE:该处A0代表线路上最先到达的bit,A17代表最后到达.

可见小端系统是一个移位性质的寄存器,先到达的bit放在byte内的高位地址。(所谓小端地址的定义是:most significant byte in high address。所以 0x01 0x02 0x03 0x04,代表的数据是0x04030201.同样的道理,在字节内部,物理地址高的bit代表高位。所以如果一个binary stream在字节中从左到右的顺序是 10001010,那么它代表的byte数据应该是0x51,而不是0x8A.)

可见,属于同一个域A的数据在物理地址上面并不连续了。为了处理这个问题,我们必须把这32bit数据变换为小端Byte顺序,既
------------------------------------------------------------------------------------
| B13 ... B6 | B5 ... B0 A17 A16 | A15 ... A8 | A7 ... A0 |
    byte3            byte2            byte1       byte0

low address --------------------------------> high address
-------------------------------------------------------------------------------------

这样一来,同一个域的数据在物理上就连续了。使用C语言bit field的定义
struct{
    UINT32 B:14;
    UINT32 A:18;
}
这样 B 对应了这个32bit结构的低14bits, A 对应了高18bits。编译器只需要简单的截断移位就可以处理这两个域了。

2. 大端

大端系统正好相反,在byte顺序上是高位数据对应least significant byte。在byte内部靠近高位地址的bit对应least significant bit.

数据到达内存的时候
------------------------------------------------------------------------------------
| A0 ... A7 | A8 ... A15 | A16 A17 B0 ... B5 | B6 ... B13 |
    byte0        byte1            byte2           byte3

low address --------------------------------> high address
-------------------------------------------------------------------------------------

大段在物理地址上和传输顺序(文档描述)一模一样,比较容易理解。这也是大端结构设计的初衷。

于是只需要在bit field声明如下结构:
struct{
    UINT32 A:18;
    UINT32 B:14;
}
这样就一目了然了。

3.总结

这些内容如果不是经常使用非常容易混淆,因为小端需要有两个转换,首先是byte顺序,其次是bit顺序。使用的时候最好在pc上多做实验。


对于小端,我之前理解的有个误区,比如0x12345678

我认为仅仅只字节的位置倒置,其实是这个数字的bit全部倒置,而c对一个字节内的操作是不可见的,就是你以为是正序的,其实它是逆序的。

比如,int c = 5;

while(c) {
        cout<<(c&1)<<endl;
        c =c >>1;
    }

我们发现字节是正序排列的,即0101。其实在内存中bit是逆序排列的,1010,只是操作系统对我们隐藏了对bit的操作。


例如,对于这个题目:

typedef struct bitstruct
{
int b1:5;
int :2;
int b2:2;

} bitstruct;
void main()
{
bitstruct b;
memcpy(&b, "EMC EXAMINATION", sizeof(b));
printf("%d,\n", sizeof(b)); //
输出4
printf("%d, %d\n", b.b1, b.b2); //
输出5-2

}


在内存中存储的是,0x45 4d 43 20

并且在每个字节内的bit是逆序排列的


内存分布

1

0

1

0

0

0

1

0

1

0

1

1

0

0

……

E

M

0

1

2

3

4

5

6

7

8

9

10

11

12

13

……



b.b1是 10100,对其再次逆序,得00101,得5

b.b2 = 01,对其再次逆序是 10,取其补码是-2


http://blog.csdn.net/ermuzhi/article/details/7940280

99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。使用位域的主要目的是压缩存储,其大致规则为:

1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。



和结构体一样,位结构体也是按照成员的最大长度字节来对齐分配空间的。


测试:
struct test
{
char a:1;
char :2;
long b:3;
char c:2;
};
test t1;
int len=sizeof(t1); //len=4,前两个位域共用一个char空间,按第三个位域long扩展为4字节空间,最后一个位域占用一个char空间,按最长位域空间扩展为4字节。

structtest
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
test t1;
int len=sizeof(t1); //len=2,第一位域占用一个char空间,按第二个位域short扩展为2字节空间,最后一个位域占用一个char空间,按最长位域空间扩展为2字节。

如果位域上的整形范围值是0,则下个位域从新的字节开始,前一字段后面空出的所有字节都不使用(即使是同类型的位域),如:

structbs
{
unsigned a:4;
unsigned :0; // 空域
unsigned b:4; // 从新字节开始存放
unsigned c:4;
} ;

sizeof(unsigned)== 4

上面这个位域定义中,a占第一字节的4位,第一个字节的后4位以及后面的3个字节都填0表示不使用,b从新的字节开始,占用4位,c占用4位。上面位结构体大小为:8


struct test
{
char a:1;
char :2;
char b:3;
long c:2;
};
test t1;
int len=sizeof(t1); //len=4

struct test
{
char a:1;
char :2;
char b:3;
char c:2;
};
test t1;
int len=sizeof(t1); //len=1

{// test2
union V {
   struct X {
      unsigned char s1:2;
      unsigned char s2:3;
      unsigned char s3:3;
   } x;

   unsigned char c;
} v;

v.c = 100;
printf("%d", v.x.s3); 

}

结果:3,从低位开始分配位空间

 



#include    <iostream>  
using   namespace   std; 
struct   A    
{    
char   t:4;    
char   k:4;    
unsigned   short   i:8;    
unsigned   long   m;    
}    

main() 
{    
struct   A   a; 
a.t= 'b '; 
cout    < <   a.t < <   endl; 
              
}

 

 

a.t = 'b';效果相当于 a.t= 'b' & 0xf;

'b' --> 01100010
'b' & 0xf -->>00000010
所以输出Ascii码为2的特殊字符

 

下一个例子:

 

 

 union Test {
    char a[5];
    short b;
};


int main()
{
Test t;
    t.a[0] = 256;
    t.a[1] = 255;
    t.a[2] = 0x81;
    t.a[3] = 2;
    
    printf("%d",t.b);


return 0;
}

首先256用int存储,所以a[0]只能取其后面一个字节,为0

最终a的前两个字节的内存分布是:

ff00

取其补码为-256



你可能感兴趣的:(大端 小端)