背景
早在linux操作系统诞生开始,c语言作为linux系统的编程语言主力,它为后续的其他高级编程语言(如c++、java)提供了很多语言级的语义和协议规范。
数组做为linux操作系统最基本的数据结构之一,便是其中的一项语言级高级特性,深入理解数组有助于大家更深入的了解计算机系统原理。
寻址:从0开始
首先,我们了解下数组的基本特性
1、线性表结构
2、一组连续的内存空间
3、存储相同类型数据
由数组的基本特性可知:数组所申请的内存是一段连续的内存地址,其存储的是相同类型的值。
从其数组内存模型定义来看,数组的访问是从首地址(偏移量为0)开始,如果想访问下一个数组元素,需要把位置偏移,也就是数组内存寻址时,需要把偏移量变为1,以此类推。
计算数组内存寻址公式:
array[i] = base_address + i * data_type_size
参数说明
- base_address 首地址array[0] ,数组第一个元素内存地址
- i 为偏移量
- data_type_size 数据类型字节大小
举个例子:
1、定义一个int 数据类型的数组,a为变量,数组长度为5
int[] a=new int[5]
2、假设数组地址:
- 第一个内存地址为:1008
- 第二个为:1009
- 第三个为:1010
- ...
3、寻址:由公式:array[i] = base_address + i * data_type_size
a[0]——把a拿到的内存地址:
1008 + 0 = 1008 (指向第一个内存地址)
a[1]——把a拿到的内存地址:
1008 + 1 = 1009 (指向第二个内存地址)
a[2]——把a拿到的内存地址:
1008 + 2 = 1010 (指向第三个内存地址)
以此类推。。。
关心话题(一):数组下标为什么不从1开始
cpu消耗
如果数组从1开始编号,公式为a[i] = base_address + (i -1) * data_type_size,对cpu而言,”i -1“就多了一次减法指令。
历史原因
c语言的设计者使用了0开始编号,后续的很多高级编程语言也就沿用了这个规范,降低额外的学习和理解成本。
关心话题(二):随机访问
由于数组是线性结构,那么根据首地址和偏移量就可以计算出任意数组元素的内存地址
array[i] = base_address + i * data_type_size
java面试题:数组和链表的区别
1、链表适合插入和删除,时间复杂度是O(1)
2、数组适合查找,查找的时间复杂度为O(1)
究其原因是数组支持随机访问,根据下标随机访问的时间复杂度是O(1),而链表需要从头开始寻址,直到找到对应的元素地址才结束,其复杂度为O(logn) 。
关心话题(三):集合能替代数组吗?
数组和集合最大一个区别于在于数组能直接存储基本的数据类型(如int、float、long等);而集合只能通过装箱和拆箱存储基本数据类型,代价是需要消耗性能。另外使用多维数组时,用数组会直观一些。
jvm标记清除算法
为了避免数组大规模的数据搬移,可以将k位置的数据直接搬移到数组末尾位置,再把新数据放到k位置。删除时,可以将要删除的数据标记下来,当数组没有足够的空间时,将标记的数据统一删除,就可以提高很大的性能,也就是jvm标记清除垃圾回收的思想 。
总结
大部分高级程序语言的数组下标是从0开始的,而python数组下标却可以支持负数 。