热度 20
2013-1-29 14:51
2152 次阅读|
0 个评论
关于上文中遗留的两个问题,首先关于内存对齐。 为什么需要进行内存对齐呢?接触过单片机知道,单片机属于8位机,所以每次读写数据都是以8bit来进行的,所以在内部存储器和外部的存储器都是以字节为单位来寻址的。回到我们PC中,物理层面上每个地址也是对应着8bit数据的,也就是一个字节。但对于操作系统来说就有些不一样了,而且我们PC中包含的存储单元数量远远大于单片机系统中所使用的,所以如果每次访问变量时可从任何地址开始,变量排列只是按照单纯的顺序规则排列那对CPU来说效率是相当低的。所以我们需要对各种类型的变量按照一定的空间规则排列,用来提高我们的效率。 举个例子,在一个32位系统中,每次读取数据都是从偶地址开始读取。先定义一个字节型,再定义一个int型,如果按照顺序排列,那当读取int型时,则需要读两次以后,再将数据拼凑才行。而,如果是按照4字节对齐,那读取int型数据也就只需要1次就可以了,效率上提高了很多。 对于具体的操作系统的具体对齐规则我们不一定要完全了解,只需记得,在内存中,各类型的变量并不是单纯的顺序排列的,而是需要对齐处理的。 那我们再看看上一文中的第二个问题,我们输出s5这个字符串时显示的是“123456烫烫烫烫烫Hello”,这里面的“Hello”显然是我们s4中定义的那个“Hello”吗?两者是怎样的关系? 按我们常理来说,我们先定义了s4,随后再定义了s5,s4应该在s5存放之前才对。因为s5在定义时没有在尾部加‘\0’,所以如果输出字符串格式则会知道遇到‘\0’时才停止。那又怎么会跑到前面s4那里去呢?难道那个Hello不是s4中的?错,它就是s4中的,这个我们可以实验验证,修改s4内容,看看s5的输出是否也同样修改。那这到底是怎么回事? 开始博主也不理解,于是在调试窗口把两个变量的地址调出了看了看才惊奇的发现,s4的地址竟然比s5要大!是的,编译器给变量分配内存时,是从高往低分配的。下面,再做个例程来详细说说编译器是如何给变量分配内存的。 // memory_test.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" char gc1; char gc2 = 'g'; int gn1; int gn2 = 100; char gc3; char gc4 = 'c'; int _tmain(int argc, _TCHAR* argv[]) { char lc1; char lc2 = 'l'; int *pn = (int *)lc2; int ln1; int ln2 = 9; char lc3; char lc4 = 'c'; printf("Globals:%c %d %c %d %d %d %c %d %c %d\n", gc1,gc1,gc2,gc2,gn1,gn2,gc3,gc3,gc4,gc4); printf("Locals:%c %d %c %d %d %d %c %d %c %d\n", lc1,lc1,lc2,lc2,ln1,ln2,lc3,lc3,lc4,lc4); return 0; } 程序**定义了四种类型的变量,已初始化的全局变量、未初始化的全局变量、已初始化的局部变量、未初始化的局部变量。数据类型则选择了int型和char型。我们先来看看输出结果: 可见,对于全局变量未初始化数据为0。而对于局部变量,未初始化则为未知数。我们再看看所有变量所处的地址位置: 从中我们可以大概看出,所有变量被分配到了三个区域: 初始化的全局变量。 未初始化的全局变量。 所有的局部变量。 我们先说局部变量,可见对于所有的局部变量,地址是从高到低分配的,也就是说,先定义的变量处于地址较大的内存中,而随后的变量依次往地址小的内存中分配。所以我们前一文提到的那个问题就迎刃而解了。 但我们再看全局变量的内存情况,所有的全局变量被分配到了地址基数较大的内存中(也就是说,全局变量的内存分配比局部变量的早,不管全局变量是在代码的什么地方定义的),而未初始化和初始化了的全局变量并不是统一处理的,他们分别被区分地分配到两个不同区域。再看单个全局变量的地址分配,如上例我们可以看到,gc1和gc3虽然不是顺序着定义的,但是内存地址确实紧挨着的,随后才是gn1。说明,我们的全局变量在内存分配时和代码上定义的顺序没有直接关系,编译器会自动将数据类型一样的变量分配相连地址,这样做的目的则是为了节省内存。我们知道,对于CPU来说,全局变量的访问速度是最快的,所以它需要被存放在一个相应的内存空间,便于频繁快速地访问,同样的,这样的内存大小是有限的,所以需要在分配时考虑占用效率。(这样的方式也告诉我们,如果我们自己想写出更高效的代码,在局部变量定义时,也应该尽量将同样数据类型的变量放在一起。)