关于大小端存储方式问题的思考

来源:互联网 发布:漂亮的谐音网络用语 编辑:程序博客网 时间:2024/05/15 23:04

关于大小端存储方式问题的思考

注:以下内容来自百度百科。   

大端模式

  所谓的大端模式,是指数据的高位,保存在内存的低地址中,而数据的低位,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
  例子:
  0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
  0000440: b484 6c4e 004e ed00 0000 0000 0100 0000
  在大端模式下,前16位应该这样读: e684
  记忆方法: 地址的增长顺序与值的增长顺序相反

小端模式

  所谓的小端模式,是指数据的高位保存在内存的高地址中,而数 据的低位保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
  例子:
  0000430: e684 6c4e 0100 1800 53ef 0100 0100 0000
  0000440: b484 6c4e 004e ed00 0000 0000 0100 0000
  在小端模式下,前16位应该这样读: 84e6
  记忆方法: 地址的增长顺序与值的增长顺序相同

为什么有大小端模式之分

  为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于 8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于 大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

测试编辑器属哪种模式

  下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:

       short int x;

  char x0,x1;
    x=0x1122;
  x0=((char*)&x)[0]; //低地址单元
  x1=((char*)&x)[1]; //高地址单元
  若x0=0x11,则是大端; 若x0=0x22,则是小端......

  上面的程序还可以看出,数据寻址时,用的是低位字节的地址。

 

我的思考:

第一个简单的问题:

 我想通过联合体来验证下大小端模式

代码如下:

int  Checksystem()

{

union check

{

   int i;

   char ch;

} c;

c.i = 1;

return (1 == c.ch);

}

分析:众所周知,union 维护足够的空间来放置多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在union中所有数据成员公用一个空间,同一时间只能存储其中一个数据成员,所有的数据成员具有共同的起始地址。 其实,union 主要用来压缩存储空间,如果一些数据不可能在同一时间被用到,则可以使用union。

  变量i占四个字节(在VC6.0里是这样的),但是本例中只有一个字节有数 值为1。由于是联合体,所以所有数据成员具有共同的起始地址,那么c.ch就应该是低端的数据,如果为1,那么就证明是小端模式(字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中)。

 

第二个不太简单的问题:

 #include<stdafx.h>

int main()

{

   int a[5] = {1,2,3,4,5};

   int *ptr1 =(int*)(&a+1);

   int *ptr2 = (int *)((int)a+1);

   printf("% x,% x",ptr1[-1],*ptr2);

   return 0;

}

这个输出会是什么呢?

先把答案贴上: 5,2000000 

以上答案是在我的机器上VC6.0控制台程序测试的。

 

 实际调试时内存范围如下: a                 0x0012ff6c

                                                   ptr1            0x0012ff80

                                                   ptr2            0x0012ff6d

    由上可见,ptr2 是指向数组整个地址中的第二个byte地址,整个数组一共5*4Byte 即一共20个字节。ptr1 地址有点诡异,相减后会发现0x80-0x6C = 0x14,也就是十进制的20;

      所以 int *ptr1 =(int*)(&a+1);  将a指针的地址取出来为&a ,然后加了一个1 其实是加了整个数组的五个int长度每个四个字节,共20个字节。

因此,ptr1[-1] 刚好是数组a中的最后一个元素,即5.

而 *ptr2 为什么是2000000(注意是六个0,而不是七个0)呢???

        好,接下里我们看下内存中 数组中的数据到底怎么存的

          0012FF6C  0100 00 00 02 00 00 00 ........
          0012FF74  03 00 00 0004 00 00 00  ........
          0012FF7C  05 00 00 00 C0 FF 12 00  ........
          0012FF84  69 12 40 00 01 00 00 00  i.@.....

     以上来自VC memory调试窗口,那么0x0012ff6d地址对应的四个字节刚好是上面紫色部分标示,由于这里采用的是小端模式,所以从高到低地址(紫色从右到左)依次就是数据的从高到低,所以,输出是2000000(切记 是六个0而不是7个0,为什么呢??? 以数组第一个数为例,举例如下:00 00 00 01 那么同理,紫色段为 02 00 00 00,所以是 2000 000 而不是常规思维想的 直接把紫色部分倒过来20000000,其实,这种常规思维可以以第一个数为例,那么应该是00 00 00 10,显然不是1 而是2。

  总结上面的小段,每个字节 即每个Byte 以01 为例,也分高低,左边为高,右边为低。而内存窗口中,左边为低,右边为高。 以上理解起来有点难度,但是还算可以。希望有兴趣的与我多交流。

                                                                                                                                                                                            damoliuyunli@163.com

                                                                                                                                                                                                                20120908

 

 

 

 

原创粉丝点击