1、内存 肖舸标签: 内存 2009-04-28 13:38 内存可以说是 C 和 C+语言学习的关键点。这里说一点我的理解,一家之言,欢迎拍砖哈。内存要想理解透彻,首先要理解内存编址。即不同的内存条,内存模块,插到机器上,具体对应的内存地址是多少。最开始的 PC 机, IBM PC XT,只有 640k 内存。IBM 是这么规划的,最低的 128k,是 BIOS的地址,毕竟 BIOS 也是汇编语言,它也需要合法地址,才能被 CPU 正确运行。512k640k,被定义为端口映射地址,即这部分地址,可能对应某一个外设上的地址,方便程序直接访问设备。其中,最重要的,就是显卡,当初的显卡单元都比较小,如
2、单色显卡只有 2k显存,就映射在这个地址段。呵呵,当年比尔盖茨,开了个世纪有名的黄腔,就是家用电脑,640k 内存足以。就是这么来的,可现在呢?你的显存都不止 640k,可见,伟人也有犯浑的时候。这样显然不方便,因为就在几年后,286 时代,内存已经 1M 了。这就麻烦了,这 1M 的第一个 128k 不准用,中间还有个 128k 不准用,好端端的连续内存,就被切成两块,痛苦啊。而那会 CPU 也是笨得可以,16 位的 CPU,Intel 居然在 PC XT 上面用的是 8 位地址总线,这下死翘了,所有的内存被分割为 64k 一块块的小块,叫段。每个程序模块,必须小于 64k,否则没法跳转。以
3、前 DOS 下可以执行的二进制文件分为两种,com 和 exe,com 就是不能大于 64k 的,因为它文件格式里面没有段修饰,因此,只能一段完成,64k 。这样,程序员十分的不方便,写程序,稍微大点的数组,就要考虑分段访问,没办法,数组下标不能超过 64k。另外,对于 640k 以上的地址访问,人们想出了一个很笨的方法,把 640k1024k 这 384k,也切成一块一块的,每块映射相同的内存区域,靠一个 IO 切换来访问不同的块。那会还没有想到更好的支持 1M 以上内存的方法,只能这么办。这样,程序员成了个苦恼的职业,既要做软件编程,还要随时关注自己的数据是否越界,痛苦死了。当然,程序员也
4、不会坐以待毙,这期间,他们自己想了很多方法,比如用底层模块来解决段切换问题,对上提供一层连续编址的虚拟地址来访问等等。ok,到了 80386,32 位了,大家总算长出了一口气,这个 CPU 地址总线有 32 位,可以直接编址 4G 内存。但是,问题来了,PC 机已经做成这个样子了。当然,可以重新开发一款计算机,连续编址,4G 内存,很爽,只是,这不是 PC 机了。这又让人痛苦死了,明明有能力做连续编址,跨越段界限,但还是不能这么做,因为要支持老的程序。程序员又开始想办法,基本思路就是用虚拟地址来代替实际地址,后来又想到了,既然都是虚拟的,那我们可不可以把一块磁盘文件也虚拟成内存,这样,我们给够
5、 4G,这不是更爽,底层再用一定算法来处理动态转换的效率优化。这样,在 93 年左右吧,有一个很有名的 C+语言,Watcom C+出世了。这个语言,是 Dos下第一款支持 4G 内存的 C+语言。这是 Sybase 开发的,它底层使用了一个虚拟内存管理模块,是另外一家公司开发的,叫 Dos /4G,显然,就是 DOS 程序使用 4G 内存的解决方案。还记得 DOOM、Cchar* pBuffer=malloc(10);/.这里面,Func 的代码,在栈空间,其实是在基栈了。2G 的最低int i,这个 i,在浮动栈。基站上方,也还是 2G 底部。pBuffer 指向的内存,由于是 mallo
6、c,因此是堆空间,在 2G 的高端。new 和 malloc 其实是一样的,都是 malloc 的,但 new 支持对象,要自动调用构造函数。不过这里也说明一点,new 出来的对象,是运行期对象,其内部成员变量,其实是在 2G 的高端,堆空间里面。写 C 和 C+程序,需要对内存的分配非常敏感,随时关注自己使用的变量,是属于编译期间的基栈,还是运行期的浮动栈,还是堆。比如,我们要启动一个线程,这个线程函数,肯定是在基栈了,编译器就定好的,但是我们希望线程访问一个运行期的动态地址,比如要传递一个参数给它。我们就不能简单把一个函数内部的变量地址传给他,由于是函数内部变量属于浮动栈,函数返回,浮动栈就自动拆除,而线程启动是异步运算,就是一个函数启动线程,很可能这个函数已经返回了,线程还没有开始运行。因此,就绝对不能使用函数内部变量给线程传参,只能使用堆空间,用 malloc 的一块地址来传参数,再由线程函数收到后,free 掉。这是唯一一个,不遵守“谁分配,谁释放 ”原则的特例。我把它叫做 “远堆传参”。很多初学线程的朋友,线程写出来就挂掉,就是这个地方出了问题。但是,程序表面看起来一切正常,呵呵,所以内存很重要,因为它里面基本上都是隐式 bug,很难用肉眼看代码看出来。