self-confidence,the source of all the power

导航

大小端存储

前言

  一般来说,底层开发人员接触到这大小端存储的概念比较多,特别是嵌入式开发人员。我们知道,不管什么类型的操作系统都需要对数据或文件进行存取操作,但由于各个系统的存储方式会因为其CPU架构不同而有差异。对于所有CPU来说,它们大概存在两种存储方式:大端字节序(big-endian),小端字节序(little-endian)。

  常见的CPU架构的字节序吧:
      Big Endian : PowerPC、IBM、Sun
      Little Endian : x86、DEC

定义:
   a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端
   b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端
   c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

记忆方法:

  系统存储数据的时候是以字节为单位的,因此在处理short,int等大于1个字节的数据类型时,就会可能存在存储位置不一样。系统申请内存都是从低位开始的,因此以在低位存的数据来判断或命名该方式,所以低位存高位数据的方式叫大端字节序,低位存低位就叫小端字节序。这样理解的话是不是方便记忆呢?

  至于前面提到的网络字节序,这个概念来自网络编程的一个术语,实际上它就是大端字节序。呵呵,为什么它非要是大端字节序而不是小端字节序呢?关于这个问题,我个人有这样的一个理解,至于对不对大家自己去判断吧,我的理解是这样的:

  网络字节序处理的主要是通信时的目标地址或源地址,即平时我们说的IP地址,它是一个4个字节的数字类型(uint32_t).假设一个主机IP地址为192.68.1.1,那从高到低位它的数据位是192 68 1 1,系统在读数据的时候是从低到高开始读取的。如果将IP按大端字节序存取,则从低位地址到高位地址依次存放数字:192,68,1,1。这样在取数据的时候刚好能组成最初的IP形式:192 68 1 1,因为网络地址IP需要的就是这四个数字这样的形式,也方便将之转换为字串。

 

存储例子  

  下表是存一个4字节的0x12345678,不会画图将就着看吧,地址是从低到高对应的表格从上到下:

address big-endian  little-endian
0x0000 0x12 0x78
0x0004   0x34 0x56
0x0008 0x56 0x34
0x0012 0x78 0x12

  通过这个表格的对比是不是比较清楚大小端的存储方式了呢,那么大小端字节序的转换需要怎么做呢?很简单,就是将高位地址里的内容与低位地址内的内容两两交换就可以了。  

 如何判断CPU采用何种字节存储顺序(大小端)

  常见的是联合体判断法,代码如下:

 1 bool isBigEndian()  
 2 {  
 3     union
 4     {  
 5        int a;  
 6        char b;  
 7     }num;  
 8 
 9     num.a = 0x1234;  
10     return ( num.b == 0x12 )   
11 }


大小端转换

  在面试的时候被问到了这个题,写一个宏来对一个16位机进行大小端转换操作,当时没有写出来,只是回答:将高低地址内容交换就OK了。后来想想其实这个问题很简单,用移位的方法非常好实现,而且这样做效率高,速度快。

  移位原理比较简单,相当于是将原数据移位后再保留对应的数位,将其它位置0,将每个字节的内容都准备好后,最后再将它们相或就得到转换后的数据了。
1 //16位机
2 #define __SWP16(A)   (( ((uint16)(A) & 0xff00) >> 8)    | \  
3 (( (uint16)(A) & 0x00ff) << 8))  
4 
5 //32位系统,大小端转换
6 #define __SWP32(A)   ((( (uint32)(A) & 0xff000000) >> 24) | \  
7 (( (uint32)(A) & 0x00ff0000) >> 8)   | \  
8 (( (uint32)(A) & 0x0000ff00) << 8)   | \  
9 (( (uint32)(A) & 0x000000ff) << 24)) 

 

posted on 2013-03-30 20:08  漩涡鸣人  阅读(538)  评论(0编辑  收藏  举报