专栏学习笔记:为什么很多编程语言中数组都从0开始编号?

两个概念

线性表

除了数组,链表、队列、栈等也是线性表结构。

非线性表

二叉树、堆、图等都是非线性表,之所以叫非先行是因为 数据在非线性表中并不是简单的前后关系。

数组越界问题在java和c中的差别

由于在c语言中,只要不是受限的内存,所有的内存空间都可以是自由访问的。所以当C语言中数组的下标超过了数组的长度,并不会直接出错,而是会出现其他奇怪的问题。

1
2
3
4
5
6
7
8
9
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}

比如这段C代码,由于循环条件写成了<=3所以 在执行3次以后,数组下标访问越界,这个会造成死循环一致输出hello world问题,这里由于涉及到其他方面的原理,暂不深究,这里贴出作者的讲解和其他读者的评论给大家参考。

而在java中的话,虚拟机就会抛出数组越界异常。

高级语言中的容器

以java为例,作为java程序员可能每天都在使用的ArrayList,就是对数组进行一定程度的封装,比如自动扩容,迭代器等 方便程序员的使用。

容器与数组的选择

既然容器这么好用,那数组是不是就全无用武之地了呢?

当然不是,有些时候,使用数组会更加合适。下面列出的几种常见的情况:

  1. 由于java的ArrayList只能存储Integer、Float等包装类型,不能存储基本类型, 在自动拆箱装箱会有一定的性能消耗。

  2. 如果事先已知数据的大小,那么使用数组会更加方便,在声明数组的时候直接指定好它的大小。研究过ArrayList源代码的同学应该知道,ArraylList底层也是数组,如果不指定初始长度的话,每一次快满了的时候都会自动扩容,而扩容就是使用了数组复制,想象一下,如果你的List有1G的数据,容器扩容到1.5G,就要把这1G的数据复制过去,是不是想想都很费时间。当然,使用ArrayList也可以事先指定好一个大小。关于这一块的源码分析可以看别人的另外一篇文章:https://blog.csdn.net/fighterandknight/article/details/61240861

为什么数组从0开始编号

从存储模型的角度来看,数组的下标说是偏移量更为准确,它的英文offset也正是此意。偏移量就表示了相对于首地址偏移了多少,学过C语言的同学应该记得,如果把数组赋给一个变量, 那它的指针就是指向数组的头节点,而头节点的偏移量就是0。是不是很好理解了?