为什么索引在'C'中从零开始?

Nit*_*eek 146 c arrays

为什么数组中的索引在C中从零开始而不是1?

Mas*_*uso 111

在C中,数组的名称本质上是指针,对内存位置的引用,因此表达式array [n]指的是远离起始元素的内存位置n元素.这意味着索引用作偏移量.数组的第一个元素完全包含在数组引用的内存位置(0个元素之外),因此它应该表示为array [0].

了解更多信息:

http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html

  • 数组的名称是数组的名称; 与常见的误解相反,数组在任何意义上都不是*指针.数组表达式(例如数组对象的名称)通常是*但不总是*,转换为指向第一个元素的指针.示例:`sizeof arr`产生数组对象的大小,而不是指针的大小. (16认同)

Ani*_*han 100

这个问题是在一年前发布的,但是这里......


关于以上原因

虽然Dijkstra的文章(以前在现在删除的答案中引用)从数学角度来看是有意义的,但在编程方面它并不那么重要.

语言规范和编译器设计者所做出的决定是基于计算机系统设计者决定从0开始计数.


可能的原因

引自丹尼科恩的和平辩护.

对于任何基数b,仅当编号从0开始时,第一个b ^ N个 非负整数由正数N个数字(包括前导零)表示.

这可以很容易地测试.在base-2中,取2^3 = 8 第8个数字是:

  • 8(二进制:1000)如果我们从1开始计数
  • 7(二进制:111)如果我们开始计数为0

111可以使用3位表示,而1000需要额外的位(4位).


为什么这是相关的

计算机存储器地址具有2^NN位寻址的单元.现在,如果我们从1开始计数,则2^N单元格需要N+1地址线.需要额外的位才能正确访问1个地址.(1000在上述情况下).另一种解决方法是让最后一个地址无法访问,并使用N地址线.

与开始计数为0相比,两者都是次优解决方案,这将使用精确的N地址线保持所有地址的可访问性!


结论

开始计算的决定0已经渗透到所有数字系统,包括在其上运行的软件,因为它使代码更容易转换为底层系统可以解释的内容.如果不是这样,那么对于每个阵列访问,机器和程序员之间会有一个不必要的转换操作.它使编译更容易.


引用论文:

在此输入图像描述

  • 如果他们刚刚删除了0位...那么第8个数字仍然是111 ... (2认同)
  • 你是否真的建议修改基本算法以使其适合?难道你不认为我们今天拥有的是一个更好的解决方案吗? (2认同)

Dou*_* T. 26

因为0是指向数组头部的指针到数组的第一个元素的距离.

考虑:

int foo[5] = {1,2,3,4,5};
Run Code Online (Sandbox Code Playgroud)

要访问0,我们会:

foo[0] 
Run Code Online (Sandbox Code Playgroud)

但是foo分解为指针,并且上面的访问具有类似的指针算术方式来访问它

*(foo + 0)
Run Code Online (Sandbox Code Playgroud)

这些天指针算术不经常使用.回过头来的时候,这是一个方便的方法来获取一个地址并将X"整数"从该起点移开.当然,如果你想留在原地,你只需加0!


Bra*_*vic 22

因为基于0的索引允许...

array[index]
Run Code Online (Sandbox Code Playgroud)

......实施为......

*(array + index)
Run Code Online (Sandbox Code Playgroud)

如果index是从1开始的,编译器需要生成:*(array + index - 1),而这个"-1"会损害性能.

  • 你提出了一个有趣的观点.它会伤害性能.但是,如果使用0作为起始指数,性能是否会显着?我对此表示怀疑. (2认同)
  • @FirstNameLastName基于1的索引没有优于基于0的索引,但它们执行(稍微)更糟.无论增益多么"小",这都证明了基于0的索引.即使基于1的索引提供了一些优势,但是在C++的精神中选择性能而不是方便.C++有时候用在最后一点性能很重要的环境中,而这些"小"的东西很快就会加起来. (2认同)
  • 您应该使用array-1的地址作为基地址,而不是减去1.这就是我曾经在编译器中做过的事情.这消除了运行时减法.当您编写编译器时,这些额外的指令非常重要.编译器将用于生成数千个程序,每个程序可以使用数千次,并且在n平方循环内的多行中可能发生额外的1条指令.它可能会浪费数十亿的浪费周期. (2认同)

pro*_*rmr 12

因为它使编译器和链接器更简单(更容易编写).

参考:

"...通过地址和偏移量引用内存直接在硬件上直接表示在所有计算机体系结构上,因此C中的这种设计细节使编译更容易"

"......这使得实施更简单......"

  • @phkahler:错误在作者和语言中调用数组索引作为索引; 如果你认为它是一个偏移量,那么从0开始对于外行人来说也是很自然的.考虑时钟,第一分钟写为00:00,不是00:01不是吗? (4认同)
  • +1 - 这可能是最正确的答案.C早于Djikistras论文,是最早的"0开始"语言之一.C开始生活"作为一个高级汇编程序",并且K&R可能希望坚持与汇编程序中的方式一致,在那里你通常会有一个基地址加上从零开始的偏移量. (3认同)
  • 我不会downvote但是正如progrmr在上面评论的那样,可以通过调整数组地址来处理基数,因此无论基本执行时间是否相同,这在编译器或解释器中实现都是微不足道的,所以它并不能真正实现更简单的实现.见证Pascal你可以使用任何范围索引IIRC,它已经25年了;) (2认同)

R..*_*R.. 5

出于同样的原因,当它是星期三,有人问你到星期三多少天,你说0而不是1,当它是星期三,有人问你到星期四多少天,你说1而不是2.

  • 回复:"从1而不是0开始的编号数组是针对严重缺乏数学思维的人." 我的CLR版"算法简介"使用了基于1的数组索引; 我不认为作者在数学思维方面存在缺陷. (8认同)
  • 好吧,这就是添加索引/偏移的工作原理.例如,如果"today"为0且"明天"为1,则"明天的明天"为1 + 1 = 2.但如果"今天"是1而"明天"是2,"明天的明天"不是2 + 2.在数组中,只要您想将数组的子范围本身视为数组,就会发生这种现象. (6认同)
  • 召集三件事"三件事"并将其编号为1,2,3并不是一个缺陷.用第一个偏移量对它们进行编号即使在数学中也不自然.您在数学中从零开始索引的唯一时间是您希望在多项式中包含类似零次幂(常数项)的内容. (6认同)
  • 你的回答似乎只是一个意见问题. (4认同)

小智 5

数组索引始终以零开始。假设基址为2000 arr[i] = *(arr+i)。现在if i= 0,这意味着*(2000+0)等于基地址或数组中第一个元素的地址。该索引被视为偏移量,因此bydeafault索引从零开始。


Dev*_*ath 5

我有 Java 背景。我在下图中给出了这个问题的答案,我已经写在一张纸上,这是不言自明的

主要步骤:

  1. 创建参考
  2. 数组的实例化
  3. 将数据分配给数组

  • 另请注意,当数组刚刚实例化时......默认情况下,零会分配给所有块,直到我们为其分配值为止
  • 数组从零开始,因为第一个地址将指向引用(即:e - 图像中的 X102+0)

在此输入图像描述

注意:图中显示的块是内存表示


Tha*_*var 5

这是因为address必须指向element数组的右侧。让我们假设以下数组:

let arr = [10, 20, 40, 60]; 
Run Code Online (Sandbox Code Playgroud)

现在让我们考虑地址的开始和be12的大小。element4 bytes

address of arr[0] = 12 + (0 * 4) => 12
address of arr[1] = 12 + (1 * 4) => 16
address of arr[2] = 12 + (2 * 4) => 20
address of arr[3] = 12 + (3 * 4) => 24
Run Code Online (Sandbox Code Playgroud)

如果不是 zero-based从技术上讲,我们的第一个元素地址将array16错误的,因为它的位置是12