您的位置:首页 > 其它

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

2017-12-17 19:00 288 查看
热身

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. 二叉查找树指的是从节点分成两个叉的数状数据结构。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: