为什么很多编程语言中数组都是从0开始编号?

数组

数组两个特性

为什么很多编程语言中数组都是从 0 开始编号,首先先了解一下数组的概念。
数组 Array 是一种线性表数据结构,是一组连续的内存空间,用来存储一组具有相同类型的数据。数组具备以下特性:

  • 线性表,是数据排列成像一条线一样的结构,每个线性表上的数据最多只有前和后两个方向。比如,除了数组,还有链表、队列和栈。

  • 连续的内存空间和相同类型的数据,正因为这两个特点,才让数组的操作变得非常低效,要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

正因为这两个特点,可以非常高效的通过下标进行随机访问

顺序访问和随机访问的区别

顺序访问:链表在内存中不是按顺序存放的,而是通过指针连在一起,为了访问某一元素,必须从链头开始顺着指针才能找到某一个元素。

随机访问:数组在内存中是按顺序存放的,可以通过下标直接定位到某一个元素存放的位置。

寻址公式

一维数组寻址公式:

a[k]_address = base_address + k * type_size

二维数组寻址公式:
假设二维数组大小为 m*n,那么寻址公式为:

a[i][j]_address = base_address + (i * n + j) * type_size

三维数组寻址公式:
假设是 m*n*q,那么寻址公式为:

a[i][j][k]_address=base_address + (i * n * q + j * q + k) * type_size

其中, type_size 表示数组中每个元素的大小。

验证

例如,声明一个长度为 10int 类型的数组。

int arr[10] = { 0 };
for (int i = 0; i < 10; i++) {
    arr[i] = i;
}

运行结果如下,

image.png

从运行结果可以看出,计算机给数组 arr,分配了 40 字节的内存,首地址为 0x7ffeefbff4f0arr[0] 地址为:0x7ffeefbff4f0arr[9] 地址为:0x7ffeefbff514,每个 int4 个字节,故 arr[9] 结尾为 0x7ffeefbff514

C 语言中数组名代表首地址,即第一个元素的地址,a[0] 就是偏移为 0 的位置,a[k] 就表示偏移 k 个元素类型大小的位置。得出计算公式:

a[k]_address = base_address + k * type_size

结论

CPU 性能层面考虑:从数组存储的内存模型上来看,“下标”最确切的定义应该是offset。前面也讲到,如果用 a 来表示数组的首地址,a[0] 就是偏移为 0 的位置即首地址,a[k] 就表示偏移 ktype_size 的位置。如果数组编号从 1 开始计数,那这个公式就会变为:

a[k]_address = base_address + k * type_size
a[k]_address = base_address + (k-1) * type_size

对比两个公式,如果从 1 开始编号,每次随机访问数组元素就多了一次减法运算,对于 CPU 来说就是多了一次减法指令,增加了性能开销。

历史原因层面考虑:C 语言设计者用 0 开始计数数组下标,之后的 JavaJavaScript 等高级语言都效仿了 C 语言,或者说,为了在一定程度上减少 C 语言程序员学习 Java 的学习成本,因此继续沿用了从 0 开始计数的习惯。实际上,很多语言中数组也并不是从 0 开始计数的,比如 Matlab。甚至还有一些语言支持负数下标,比如 Python

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容