大小端?堆栈增长方向?数组增长方向?

来源:互联网 发布:淘宝自行车哪家好 编辑:程序博客网 时间:2024/04/29 09:51

  这个问题源自某讨论课题,闲话少说,直接上代码:

#include<stdio.h>int main(int argc, char** argv) {    int a[] = {('l'<<24) + ('l'<<16) + ('e'<<8) + 'h', 'o'};    printf("%x%x\n", a[0], a[1]);    puts((char*)a);    return 0;}

输出结果为:

6c6c65686f
hello

这个代码十分简单,就是在测试c标准对一些“神奇”的操作的默认做法以及机器内存存储方式。这里疑点最大的为数组a,为什么会是‘l’‘l’‘e’‘h’‘o’呢?分析一下:

1. 字符在进行数值运算时将会转化为int,这点可以通过sizeof('c'-'a')来验证。(因此这里不会出现这样的warning: left shift count >= width of type [-Wshift-count-overflow])

2. 可以发现a此时在内存中的呈现(这里用assic字符代替对应的数值,也就是'l' = 0x6c、'e' = 0x65)为:

上为高地址

   \0                        -------

   \0                        |

   \0                        |  a[1]

   ‘o’                       |

------------------------------

   'l'                         |

   'l'                         |   a[0]

   'e'                        |

   'h'                        --------    <-a

3. 基于2以及第二行输出,可以得出结论:数组增长方向为从低向高地址增长, puts、scanf 等输出函数在输出字符串时输出按照从低地址到高地址的顺序

4. 由已知结论可知,栈的增长方向为从高地址向低地址增长,而堆的增长方向为从低地址向高地址增长

5. 由3、4得出结论,数组的增长方向与栈(堆)增长方向没有必然联系

6. 再看3中的a[1], 可知int a = 0x6c6c6568在写入内存中方向从左到右(从高有效位到低有效位)从低地址向高地址(小端,即little endian表示法)

经过以上分析,现在再来看

 int a[] = {('l'<<24) + ('l'<<16) + ('e'<<8) + 'h', 'o'};

 int s[] = {'h', 'e', 'l', 'l', 'o'};

产生“违和感”感的原因是什么呢?如果把数组看作许多单元的集合,那么数组的增长方向对象为数组单元之间大小端表示法为单元本身至于栈(和堆)的方向,与他们俩没有半毛钱关系。在小端表示法的机器上,数组的增长方向和单元内部的方向正好相反!当前四个字节作为一个整体int时按照小端法需要将顺序倒转过来,这是正产生“违和感”的原因。

于是现在可以写出大端法的int数组表示了:

int b[] = {('h'<<24) + ('e'<<16) + ('l'<<8) + 'l', 'o'<<24};

表现在内存中为:

上为高地址

    \0                 ----------

    \0                 |

    \0                 |   a[1]

    'o'                 |

------------------------------

    'l'                  |

    'l'                  |   a[0]

    'e'                 |

    'h'                 -----------

与小端法内存表现一致。所谓大小端,都是以byte为单位,是针对一个单元内部的byte排列方式。

现在再来看移位操作,左移操作方向为向高有效位方向,对应小端,就是向低地址方向移动,而对应大端,则是向高地址方向移动,恰好相反。

这里有几个方向,一个是堆栈方向,一个是数组方向,一个是有效位高低方向,一个是地址高低方向

至于结构体、类等,方向表现为与数组方向一致

0 0
原创粉丝点击