前言
所谓金三银四,简单的说就是每年三月四月找工作好找,而且工资高。每当到了这个季节,很多人就开始跳槽找工作,刷面试题开始面试的人就多了起来。人们又开始面试造飞机,上班拧螺丝的节奏了。
在我们刷数据结构面试题的时候总会看见一道数组和链表有什么区别的题目,很显然这是一道考的非常频繁的问题,也是非常基础的一道数据结构问题,然而他的答案也非常的显而易见。
- 数组在申请内存空间的时候需要连续的一片内存空间;而链表则不然,他只需要剩余的内存空间满足大小即可
- 数组查询快,插入删除慢;链表查询慢,插入删除快
看到这里也许你们就结束了。但是你们有没有想过以下的问题:
- 数组为什么可以快速访问
- 数组为什么需要要是一片连续的内存
- 数组为什么只能存储相同数据类型的数据
- 数组下标为什么要从0开始
如果你没有想过,那么我就带你们来解开这些问题,然而这一切都要从一个寻址公式开始。
寻址公式
什么是寻址公式,寻址公式是数组为了寻址数据所对应的内存地址的公式,公式为:
address[i] = base_address + i * data_type_size
其中base_address是在初始化数组的时候申请内存时候给的初始地址,也就是数组会从这个地址开始写数据;i就是我们数组的下标;data_type_size表示数组中每个元素的⼤⼩。我们举的这个例⼦⾥,数组中存储的是int类型数据,所以data_type_size就 为4个字节;这个公式⾮常简单,我就不多做解释了。
为什么需要一片连续的内存
当数组初始化结束,那么从寻址公式可以得出,从base_address到 base_address + (i +1)* data_type_size的内存都会被数组使用,如果这片内存里面存在着其他数据将被覆盖,所以数组在初始化的时候就会寻找(i+1)*data_type_size大小的连续空内存
为什么存储同种类型的数据
同样我们可以从寻址公式里面看出来data_type_size是一个数组初始化后就不可变的数;假设一下,一个数组存储了多种数据类型的数据,那么在存储的时候会有两种情况;第一种:当前存储的数据字节数大于之前存储的字节数,那么就会出现内存缝隙,比如,内存起始地址为10,那存入一个2个字节的数据,根据寻址公式,可以算出开始地址就是10,然后占两个字节,地址到12结束;如果再存入一个4个字节的数据,这时候根据寻址公式,可以知道开始地址是14,那个这里就出现了两个字节的空闲内存。同理:第二种情况,当前存储的数据字节数小于之前存储的字节数,我们可以看出数据会被覆盖。因此存储同一种数据类型的数据,即不会出现内存分析,又不会出现数据覆盖
数组下标为什么要从0开始
很多人都会想为什么很多语言的数据结构中的数组都是从0开始的?我们假设数组下标从1开始,那么数组寻址的公式就是:
address[i] = base_address + (i-1) * data_type_size
从这个公式里面可以看出来,相比之前的公式多做了一次减法,所以可以看出从0开始至少从性能上是比从1开始要高。数组作为一个很底层的数据结构,追求极致的性能无可厚非
总结
数组为了实现快速访问,使用address[i] = base_address + i * data_type_size的寻址公式进行寻址;因此他的内存模型必须是一片连续的内存;又为了防止出现内存缝隙或者内存数据覆盖,所以寻址公式的data_type_size必须在一个定值;因此一个数组的数据类型必须一样;追求性能的极致,所以下标从0开始