偏移量是什么意思_漫画:为什么计算机从 0 开始计数,而不是从 1 开始?

0ef0c0b70b95d081a76a63562918f9ff.png

43e1116a0e1a9eb69a56ebb9357c85c7.png

1a01c05b27f0d99e8ebf50cf8eb8b97b.png

af735a2325289f7586a18e25027a1e05.png

6f46e37f451d930fb0e74a3fe32a3e71.png

3f3683c4a56784f315057eb6c995a167.png

当我们想要写一个循环体,期望执行10次的时候,我们会使用以下方式:
for (int i=0; i<10; i++){
}
可以看到,为了保证循环10次,我们定义了一个整数变量从0开始。还有,当我们定义数组的时候,在常见的C语言、Java、Python等语言中,都是使用下标0来表示第一个元素的。

10a786de36ddd15838ae9eb218309411.png

f1b1eb0ac5043e7447af1e7134eddff9.png

0eff7cfee41a28ede612a80dbbdb20a3.png

f041e7d0897521e75a89b9297f27187b.png

90e865432d5728e4c704c9d68f0bb7dd.png

从0开始更优雅
在《为什么程序员喜欢使用0 ≤ i < 10这种左闭右开的形式写for循环?》一文中我们分析过,Dijkstra通过分析,得出在进行范围表达的时候,使用左闭右开的方式更加合理。但是,Dijkstra在分析出2 ≤ i < 13这种形式更加合理之后,他有陷入了另外一个思考,那就是:当处理长度为 N 的序列时,到底第一个元素的下标使用0还是1更加合适?关于这个分析,他的出发点很简单,那就是哪种方式更加漂亮,更加优雅。他认为,使用左闭右开的表达方式,当下标从 1 开始时,下标范围为 1 <= i < N+1;当下标从 0 开始时则是 0 <= i < N;而显然后面这种表达式更加漂亮、优雅一些。所以,他建议我们使用0作为第一个下标。

944aed9c70d18efc7136eb540c47cad1.png

5e4bf2c44441ff53e86c7329cc8c3069.png

eba79719fef21f7b4c13528f59b7f958.png

a5f4978bf6c983b4b70a991c48e671dd.png

9b9fed06fab223c752d50720e0523ccc.png

计数表示偏移量
很多人学习编程都是从C语言开始的,那么,C语言就是一个典型的0-base语言(以0作为计数的开始),其实,这一约定早在BCPL时代就是这样的了。在C语言还不叫C语言,还叫BCPL的时候,他的作者马丁·理察德就设计了数组从0开始的索引方式。当我们在BCPL(C语言)中定义数组int arr[8]的时候,编辑器会在内存中开辟一块空间(这个空间中可能包含多个内存单元)供该数组使用。为了能让数组找到编译器为自己开辟的空间,会把这块内存空间中第一个内存单元的地址(0X0000001)赋值给这个数组,当我们使用&arr的时候,就可以拿到这块地址。

578f2e9b6f973489035ef0e29b5b205c.png

BCPL最初是用IBM 7094机器编译的;它在编译时会优化这些数组索引提供的指针反参考运算(indirection),即可以通过指针取出地址中存储的值,这个特性也一直延续到今天。有了指针之后,我们可以使用int *pr = arr的方式初始化一个指针,那么,这时候,指针pr也会指向数组的内存空间的第一个内存单元的地址。

1cffb0c1c05690d62b2d19602b3eff5a.png

那有了数组和指针,想要使用这块内存第一个内存单元存储一个变量的时候,就需要想办法表示这第一个空间。那么,BCPL的作者采用了0作为数组第一个元素的下标,因为他认为,数组的下标应该和指针的偏移量是相对应的。这样在使用第一个内存单元的时候,直接使用arr[0]或者*(p+0)就可以了。

31a5174d6a50605e9a58e204110298f4.png

955f68f236501bb1d451c151d3f41c8b.png

1dd4a1c8aad95df3ffb6f615d6b11505.png

因为指针 *(p+0) 这种表达形式中的0表示的是偏移量,所以,无论数组的下标从几开始, *(p+0) 都是用于存取内存中的 p+0位址的值,也就是0X0000001这块内存单元的值。试想一下,如果使用1作为数组的起始下标,那么arr1就应该指向0X0000001这块内存,但是 *(p+1) 按照偏移量的计算方式,需要指向0X0000005这块内存。这种情况下,如果想要让 *(p+1)和arr[1] 指向同一块内存,就需要额外做一次减法指令。因为几乎所有计算机结构,都借由位址和偏移量来表示直接引用内存,所以,像C语言这种使用0做为数组的第一个下标使得语言的实现上更加容易。但是值得一提的是,在C语言流行起来之前,还是有很多1-base的编程语言的,如FORTRAN、BASIC等编程语言的数组下标都是从1开始的。随着C语言的发扬光大,很多语言都参考了C语言的做法。

938b7ffe73feb0083b5594e65d30407f.png

d34d2888b94b84771ef970cb83cf51e3.png

8c35888dcec7f73818b4ddfee34ee812.png

513fb270ea75635aa7a0f9f90480ee49.png

Python作者的解释
关于这个问题,之前也有网友在Twitter上询问过Python之父——Guido van Rossum,他给出过正面回答,我把回答内容的翻译版贴在下面:我记得自己就这个问题思考过很久;Python的祖先之一ABC语言,使用的索引是从1开始的(1-based indexing),而对Python语言有巨大影响的另一门语言,C语言的索引则是从0开始的。我最早学习的几种编程语言(Algol, Fortran, Pascal)中的索引方式,有的是1-based的,有的是从定义的某个变量开始(variable-based indexing)。而我决定在Python中使用0-based索引方式的一个原因,就是切片语法(slice notation)。让我们来先看看切片的用法。可能最常见的用法,就是“取前n位元素”或“从第i位索引起,取后n位元素”(前一种用法,实际上是i==起始位的特殊用法)。如果这两种用法实现时可以不在表达式中出现难看的+1或-1,那将会非常的优雅。使用0-based的索引方式、半开区间切片和缺省匹配区间的话(Python最终采用这样的方式),上面两种情形的切片语法就变得非常漂亮:a[:n]和a[i:i+n],前者是a[0:n]的缩略写法。如果使用1-based的索引方式,那么,想让a[:n]表达“取前n个元素”的意思,你要么使用闭合区间切片语法,要么在切片语法中使用切片起始位和切片长度作为切片参数。半开区间切片语法如果和1-based的索引方式结合起来,则会变得不优雅。而使用闭合区间切片语法的话,为了从第i位索引开始取后n个元素,你就得把表达式写成a[i:i+n-1]。这样看来,1-based的索引方式,与切片起始位+长度的语法形式配合使用会不会更合适?这样你可以写成a[i:n]。事实上,ABC语言就是这样做的——它发明了一个独特的语法,你可以把表达式写成a@i|n。但是,index:length这种方式在其它情况下适用吗?说实话,这点我有些记不清了,但我想我是被半开区间语法的优雅迷住了。特别是当两个切片操作位置邻接时,第一个切片操作的终点索引值是第二个切片的起点索引值时,太漂亮了,无法舍弃。例如,你想将一个字符串以i,j两个位置切成三部分,这三部分的表达式将会是a[:i],a[i:j]和a[j:]。

98006a21c334196b65016b0eabd5789e.png

27fdc329d1b6eee854f465988307c5ad.png

5aed36b2d979debb4a612d33b2e1b497.png

4d05c8d193a2b535bafda8ce7e002086.png
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Python,列表(list)、元组(tuple)和字符串(string)等数据类型的索引都是从0开始的,而不是从1开始的。这是因为Python的设计者Guido van Rossum认为从0开始的索引更加自然和方便。 从0开始的索引有以下好处: 1. 方便计算:从0开始的索引可以方便地通过取模运算来计算偏移量,这在编写计算机程序时非常常见。 2. 方便实现:从0开始的索引可以使编程语言的实现更加简单而高效。 3. 方便理解:从0开始的索引可以很好地反映出数据的物理存储方式,即第一个元素位于序列的最低地址。 虽然从0开始的索引可能会让初学者感到困惑,但是熟悉后可以带来更高效和方便的编程体验。 ### 回答2: Python的索引值从0开始。 Python采用从0开始的索引值,是因为其底层实现是使用C语言,而C语言的数组索引也是从0开始的。在设计Python语言时,选择了从0开始的索引值,一方面是为了与C语言保持一致,另一方面是为了简化代码的实现和理解。 从0开始的索引值可以更好地与数学的序号表示方式对应,例如,元素a的索引为0,元素b的索引为1,依次类推。这种方式更直观地展示了元素在列表的排列顺序,同时也便于进行计算。 此外,从0开始的索引值还避免了一些常见错误,例如,使用偏移量时,从0开始可以直接使用索引值作为偏移量,而不需要进行减1操作。 总的来说,Python选择从0开始的索引值主要源自其底层实现和简化代码的需要,同时也与数学的序号表示方式保持一致,使得代码更加清晰和易于理解。 ### 回答3: Python的索引值从0开始,而不是从1开始。这是因为在计算机科学,数组的索引是从0开始的,而不是从1开始的。 历史上,早期的编程语言,比如Fortran和C,都是从0开始进行数组索引。这是因为在计算机内部,数组元素在内存是连续存储的,每个元素占据一个连续的内存地址。通过使用0作为起始索引,可以更容易地计算数组元素的内存地址。 另外一个原因是简化代码。通过从0开始索引,可以直接使用偏移量来访问数组的每个元素,而无需使用复杂的偏移量计算公式。 尽管索引从0开始可能在刚开始学习编程时有些困惑,但是一旦习惯了这种索引方式,它可以带来许多好处。例如,在循环遍历数组或列表时,可以使用简单的循环变量(比如for i in range(len(array)))来访问每个元素,而不需要额外的计算或转换。 总结起来,Python选择从0开始索引是为了和其他编程语言保持一致,并且简化代码和内存寻址操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值