正文

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

1.1 效率原因

从内存模型来看,“下标”也称为“偏移”。

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

a[k]_address = base_address + k * type_size

但是钥匙从 1 开始计数,那这个公式就会变为:

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

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

数组作为非常基础的数据结构,通过下标访问数组元素又是数组上的基础操作,效率优化应做的很好,所以为了减少一次减法操作,数组选择了从 0 开始编号。

1.2 历史原因

C语言的设计者用 0 开始计数下标,之后的Java、C++等高级语言都效仿C语言,沿用了从0开始计数的习惯。

还有一些语言并不是从0开始计数的,如:Matlab。

据我所知:python还支持负数下标。

2 数组的特点

2.1 随机访问

数组是一种线性数据结构,占用一段连续的内存,存储相同类型的数据。

例如:这样一段代码

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

查看数组信息:(addr=0x7ffeefbff530, size=40, variable expression=‘arr’).

我们再来打断点看看内存情况:

看内存情况我们得到:

arr共使用40字节内存,首地址为0x7ffeefbff530

arr[0]地址为:0x7ffeefbff530

arr[9]地址为:0x7ffeefbff554

每个int有4个字节,故arr[9]结尾为0x7ffeefbff558

0x7ffeefbff558-0x7ffeefbff530 = 24 16进制

(28)16->(40)10

数组随机访问时:通过计算元素存储位置的内存地址来访问相应的元素

a[i]_address = base_address + i * data_type_size

数组支持随机访问,根据下标随机访问的时间复杂度为O(1)。

2.2 低效的“插入”和“删除”

2.2.1 低效原因

数组为了保持内存数据的连续性,会导致插入、删除这两个操作比较低效。

插入:

假设数组长度为n,要将一个数据插入到第 k 个位置,为了把第 k 个位置腾出来,我们需要将k~n这部分元素顺序的向后移动一位。

插入的时间复杂度:

最好情况:在数组的末尾插入元素,不需要移动数据了,时间复杂度为O(1)。

最坏情况:在开头插入元素,那就需要把所有的数据向后移动一位,时间复杂度为O(n)。

平均时间复杂度:(1+2+…+n)/n = O(n)。

删除:

删除操作和插入操作类似,删除了某一元素后,需要搬移数据。

和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为O(1),如果要删除开头数据,则最坏情况时间复杂度为O(n),平均时间复杂度为O(n)。

2.2.2 改进方法

插入

如果数组中的元素没有任何规律,数组只是被当作一个数据集合,在这种情况下,如果要将某个数据插入到第k个位置,为了避免大规模的数据迁移,一个简单的办法就是直接将现在第k个元素放到最后,把新元素放进来。*

例如:arr[10] = {a,b,c,d,e}要将x插入第三个位置arr[10]={a,b,x,d,e,c}

这样插入的时间复杂度为O(1),这种方法在快速排序中也会用到。

删除

在某些特殊情况下,我们并不一定非得追求数组中数据的连续性,如果我们将多次删除操作放在一起执行,效率会高很多。*

举个例子:a[10]={a,b,c,d,e,f,g,h} 如果我们要依次删除abc三个元素,需要搬移三次后面的数据,为了避免这个重复的搬移工作,可以先记录下来已经删除的数据,每次的删除操作并不是真正的搬移数据,只是记录数据已经被删除,当数组中没有更多的空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了搬移工作,这也是标记清除垃圾回收算法的核心思想。

3 数组越界问题

有如下代码:

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;
}

这段代码的结果并不是打印三行hello world,而是无限打印hello world。

为啥呢?

因为在C语言中,除了受限制的内存,其他所有内存空间都是可以自由访问的。

那为什么会无限打印呢?

根据我所学和百度的知识解释下:函数体内的局部变量存在栈区,在Linux内存布局中,栈区在高地址空间,从高到低增长,先int i = 0;int arr[3]={0};变量i和arr地址相邻,并且i地址比arr地址大,首先压栈的i,a[2],a[1],a[0],循环中arr访问越界正好到i,而此时i变量的地址是数组当前进程的,所以进行修改的时候,操作系统并不会终止进程。当然这只是32位操作系统下,64位操作系统下 默认会进行8字节对齐 变量i的地址就不紧跟着数组后面了。另外这个还和编译环境有关,对于不同的编译器,在内存分配时,会按照内存地址递增或递减的方式进行分配。如果是内存地址递减的方式,就会造成无限循环。

4 容器和数组用哪个更好

对于数组类型,很多语言提供了容器类,例如我正在学的C++中的Vector。

那什么时候用数组,什么适合用容器呢?

容器的优势就是可以将很多数组操作的细节封装起来,有的还支持动态扩容。

王争老师:如果特别关注性能,或者数据大小已知,且对数据操作非常简单,用不到容器提供的大部分方法,可以是用数组。


完,不足之处请指正。

为什么很多编程语言中数组都是从 0 开始编号相关推荐

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

    寻址方式的原因 从数组存储的内存模型上来看,"下标"最确切的定义应该是"偏移(offset)". 如果用 a 来表示数组的首地址,a[0]就是偏移为 0 的位置 ...

  2. 05 | 数组:为什么很多编程语言中数组都从0开始编号?

    什么是数组? 数组(Array)是一种线性表数据结构.它用一组连续的内存空间,来存储一组具有相同类型的数据. 线性表存储结构 连续内存空间 存储相同类型数据 优点:连续内存+相同类型数据=数组可以实现 ...

  3. 为什么数组都是从0开始?

    我们所知的大部分编程语言中,数组都是从0开始的,但你是否思考过,为什么数组从0开始编号,而不是1开始呢?从1开始不是更符合我们的日常习惯吗? 什么是数组 数组(Array)是一种线性表数据结构.它用一 ...

  4. java数组下标0_Java语言中数组元素下标从0开始。

    [单选题]规范规定,硅酸盐水泥的初凝时间不小于( ). [判断题]打开/关闭"对象追踪"的功能键是F11. [判断题]break语句的作用是结束当前的循环体. [单选题]项目标识代 ...

  5. 1. 数组:为什么数组要从0开始编号,而不是1开始呢?

    1. 数组:为什么很多编程语言中数组都是从0开始? 在大部分编程语言中,数组都是从0开始编号的,但你是否下意识想过,为什么数组要从0开始编号,而不是1开始呢? 从1开始不是更符合人类的思维习惯吗?下面 ...

  6. 为什么很多编程语言数组从0开始

    数组 什么是数组 如何实现随机访问 低效的插入和删除 警惕数组越界 关于容器和数组 什么是数组 什么是数组?它的定义就是线性表+连续的内存空间+相同数据类型的数据. 什么是线性表?线性表就是数据排成像 ...

  7. c语言如何用指针操作一维字符数组,C语言中数组和指针的互操作

    C是一种怀旧的语言,因为它的历史很久远,然而自从各种面向对象的编程语言的相续出现让它的影响力日减.当然了,这是无可非议的,但是C的高效性是其他语言无妨比拟的,所以我们有必要把握其中的精华与奥妙,也就有 ...

  8. c语言中的下标变量是什么,c语言中数组的下标从什么开始?

    c语言中数组的下标从0开始. 数组中的各元素的存储是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起.数组元素用整个数组的名字和它自己在数组中的顺序位置来表示. 例如:a[0]就表示名字为a的 ...

  9. C语言中数组长度的计算详解

    一. C语言中计算数组长度大小 C语言字符串长度的计算可以使用strlen(str); 但是对于数组长度的大小却没有相关函数可以使用: C语言数组长度的大小可以使用: int main() {int ...

最新文章

  1. php mysql 500错误日志_服务器出现500错误的时候,让PHP显示错误信息
  2. 内存256KB设备也能人脸检测,微软提出用RNN代替CNN | NeurIPS 2020
  3. 《BI那点儿事》三国人物智力分布状态分析
  4. 怎么样才能写出出色的代码
  5. VS2019安全函数scanf_s问题
  6. 信息学奥赛一本通(1037:计算2的幂)
  7. 旧闻新看 ---- 西门子为什么要收购TESIS PLMWare
  8. 【codevs1565】【BZOJ2242】计算器,数论练习
  9. 这是一篇很好的文章,学verilog的可以好好看看
  10. 【CLR】解析AppDomain
  11. 计算机组成原理—主存储器与cpu的连接
  12. 湖北省地税应用灾备中心正式启用
  13. 【leetcode】杨辉三角Ⅱ
  14. mysql建表常用sql语句
  15. Java读取配置文件Java加载不同环境的配置文件
  16. 实践三 网络嗅探与协议分析
  17. epub文件一揽子解决方案
  18. 利用archetype创建maven脚手架和新项目
  19. 关于淘宝的数据库系统
  20. 公司没大牛带,需要离职么?

热门文章

  1. 修马达的php源码,查看SKU:RB-02S087 振动马达模块的源代码
  2. Jim Williams神作:The Art and Science of Analog Circuit Design.pdf文件免费分享
  3. 《高效学习魔法书》——读书笔记
  4. 信息技术第二单元传统动画与计算机动画比较,传统动画与Flash动画设计的优缺点对比...
  5. linux docker查找镜像文件,搜索/下载/构建自定义/删除Docker镜像,运行和删除Docker容器的方法...
  6. c8051f120相关
  7. 现代ups电源及电路图集_现代UPS电源及电路图集
  8. Python-OpenCV中的图像处理 » 轮廓:入门
  9. Elasticsearch:异步搜索 - async search
  10. 淮海工学院计算机考试题库,淮海工学院数据库试卷.docx