程序是怎么跑起来的(4)---熟练使用内存

来源:互联网 发布:js给a标签添加属性值 编辑:程序博客网 时间:2024/06/05 11:47

热身
1. 有十个地址信号引脚的内存IC(集成电路)可以指定的地址范围是多少?
2. 高级编程语言中的数据类型表示的是什么?
3. 在32位内存地址的环境中,指针变量的长度是多少位?
4. 与物理内存有着相同构造的数组的数据类型长度是多少?
5. 用LIFO方式进行数据读写的数据结构称为什么?
6. 根据数据的大小链表分叉成两个方向的数据结构称为什么?

      计算机是进行数据处理的设备,而程序表示的就是处理顺序和数据结构。由于处理对象数据是存储在内存和磁盘上的,因此程序必须能自由地使用内存和磁盘。因此大家有必要对内存和磁盘的构造有一个物理上的(硬件的)和逻辑上的(软件的)认识。
     从物理上来看,内存的构造非常简单。只要在程序上花一些心思,就可以将内存变换成各种各样的数据结构来使用。

内存的物理机制很简单

      为了能够对内存有一个整体把握,首先让我们来看一下内存的物理机制。内存实际上是一种名为内存IC的电子元件。虽然内存IC包括DRAM、SRAM、ROM、等多种形式,但从外部来看,其基本机制都是一样的。内存IC中有电源、地址信号、数据信号、控制信号等用于输入输出的大量引脚(IC的引脚),通过为其指定地址(address),来进行数据的读写。

ROM(Read Only Memory)是一种只能用来读取的内存。RAM(Random Access Memory)是可被读取和写入的内存,分为需要经常刷新(refresh)以保持数据的DRAM(Dynamic RAM),以及不需要刷新电路即能保存数据的SRAM(Static RAM)。

内存的逻辑模型是楼房

      在介绍程序时,大部分参考书都会用类似于楼房的图形来表示内存,在这个楼房中,1层可以存储1个字节的数据,楼层号表示的就是地址。对于程序员来说,这种形象的解说有助于了解内存。
虽然内存的实体是内存IC,不过从程序员的角度看,可以把它假想成每层都存储着数据的楼房,并不需要过多地关注内存IC的电源和控制信号等。因此,之后的讲解中我们同样会使用楼房图。
这里写图片描述
      不过,在程序员眼里的内存模型中,还包含着物理内存中不存在的概念,那就是数据类型。编程语言中的数据类型表示存储的是河种类型的数据。从内存来看,就是占用内存大小(占有的楼层数)的意思。即使是物理上以1字节位单位来逐一读写数据的内存,在程序中。通过指定其类型(变量的数据类型等),也能实现以特定字节数为单位来进行读写。
看一个具体示例。这是一个往a、b、c这3个变量中写入数据123的C语言程序。这3个变量表示的是内存的特定区域。通过使用变量,即使不指定物理地址,也可以在程序中对内存进行读写。这是因为,在程序运行时,操作系统会自动决定变量的物理地址。

// 定义变量char a;short b;long c;// 给变量赋值a = 123;b = 123;c = 123;

      这3个变量的数据类型分别是,表示1字节长度的char, 表示2字节长度的short, 以及表示4字节长度的long.因此,同样是数据123,存储时其所占用的内存大小是不一样的。这里,我们假定采用的是将数据低位存储在内存低位地址的低字节序(little endian)方式。
这里写图片描述

简单的指针

      指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址。通过使用指针,就可以对任意指定地址的数据进行读写。虽然前面所提到的假想内存IC中仅有10位地址信号(0000 0000 00 ~ ffff ffff ff),大家现在电脑都是32位或64位。指针变量的长度也是32位或64位。

数组是搞笑使用内存的基础

      数组是指多个同样数据类型的数据在内存中联系排列的形式。作为数组元素的哥哥数据会通过连续的编号被区分开来,这个编号称为索引(index)。指定索引后,就可以对该索引所对应地址的内存进行读写操作。而索引和内存地址的变换工作则是由编译器自动实现的。

栈、队列、以及环形缓冲器

      栈和队列,都可以不勇敢指定地址和索引来对数组的元素进行读写。需要临时保存计算过程中的数据、连接在计算机上的设备或者输入输出的数据时,都可以通过这些方法来使用内存。如果每次保存临时数据都需指定地址和索引,程序就会变得比较麻烦,因此要加以改进。
栈 (FILO)先进后出
队列(FIFO)先进先出
环形缓冲器
      队列一般是以环状缓冲区(ring buffer)的方式来实现的。例如我们要用6个元素的数组来实现一个队列。这时可以从数组的起始位置开始有序地存储数据,然后再按照存储时的顺序吧数据读出。在数组的末尾写入数据后,后一个数据就会被写入数组的起始位置(此时数据已经被读出所以该位置是空的)。这样,数组的末尾就和开头连接了起来,数据的写入和读出也就循环起来了。
这里写图片描述

链表使元素的追加和删除更容易

      接下来介绍的链表和二叉查找树,都是不用考虑所以的顺序就可以对数组元素进行读写的方式。通过使用链表,可以更加高效地对数组数据(元素)进行追加和删除处理。儿通过使用二叉查找树,则可以更加高效地对数组数据进行检索。
在数组的各个元素中,除了数据的值外,通过为其附带上下一个元素的索引,即可实现链表。数据的值和下一个元素的索引组合在一起。就构成了数组的一个元素。这样,数组元素相连就构成了念珠似的链表。由于链表末尾元素没有后续的数据,因此就需要用别的值(在这里是-1)来填充。如图
这里写图片描述
这里写图片描述
这里写图片描述

二叉查找树使数据搜索更有效

      二叉查找树是指在链表的基础上往数组中追加元素时,考虑到数据的大小关系,将其分成左右两个方向的表现形式。例如,假设我们事先把50这个值保存到了数组中。那么,如果接下来的值比先前保存的数组大的话,就要将其放到右边,反正如果小的话就放到左边。但实际的内存并不会分成两个方向,这是在程序逻辑上实现的。
这里写图片描述
      为了处理二叉查找树,怎么处理比较好呢?其实数组的每个元素中只要有数据的值和两个索引信息就可以了。如下图向我们展示了如何用数组来实现的二叉查找树。二叉查找树是由链表构造发展而来的表现形式,因此在追加或删除元素方面也同样是有效的。
使用二叉查找树的便利之处在于可以使数据的搜索更有效率。在使用一般的数组时,必须从数组的开通按照索引的顺序来查找目标数据。儿使用二叉查找树时,当目标数据比现在读出来的数据小时就可以转到左侧,反之目标数据较大时即可转到链表的右侧,这样就加快了找到目标数据的速度。
只要在程序开发中多花一些新宿,我们就可以熟练地使用内存。实现栈处理、链表处理、二叉查找树处理等,这一点相比大家都清楚了。不过,大家还必须理解为什么要进行这些处理。另外,请大家牢记数组是进行这些处理的基础。
这里写图片描述

答案
1.用二进制数来表示的话是0000 0000 00 ~ 1111 1111 11(用十进制数来表示就是0~1023)
2.占据内存区域的大小和存储在该区域的数据类型
3. 32位
4. 1字节
5. 栈
6. 二叉查找树(binary search tree)
解析
1. 地址信号引脚是十个时表示2的十次幂=1024个地址。
2. 例如,C语言数据类型中的short类型,它表示的就是占据2字节的内存区域,并且存储整数。
3. 指针指的是用于存储内存地址的变量。
4. 物理内存是以字节位单位进行数据存储的。
5. 栈是一种后入先出(LIFO = Last In First Out)式的数据结构。
6. 二叉查找树指的是从节点分成两个叉的数状数据结构。

原创粉丝点击