为什么数组中的索引在C中开始为零而不是1?
答案 0 :(得分:107)
在C中,数组的名称本质上是指针,对内存位置的引用,因此表达式array [n]指的是远离起始元素的内存位置n元素。这意味着索引用作偏移量。数组的第一个元素完全包含在数组引用的内存位置(0个元素之外),因此它应该表示为array [0]。
了解更多信息:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
答案 1 :(得分:96)
这个问题是在一年前发布的,但这里有......
虽然Dijkstra's article(之前在现在删除的answer中引用)从数学的角度来看是有道理的,但在编程方面它并不是相关的
语言规范&编译器设计者是基于 计算机系统设计师决定从0开始计数。
Danny Cohen引用为和平辩护。
对于任何基数b,第一个 b ^ N 非负整数由 N 数字表示(包括 仅当编号从0开始时才是前导零。
这可以很容易地测试。在base-2中,取2^3 = 8
第8个数字是:
111
可以使用3
位表示,而1000
则需要额外的位(4位)。
计算机内存地址的2^N
个单元由N
位寻址。现在,如果我们从1开始计数,2^N
个单元格将需要N+1
个地址行。需要额外的位才能正确访问1个地址。 (1000
在上述情况中。)。另一种解决方法是让最后一个地址不可访问,并使用N
地址行。
两者都是次优解决方案,相比于从0开始计数,这将使用N
地址线确保所有地址都可访问!
从0
开始计算的决定已经渗透到所有数字系统,包括在其上运行的软件,因为它使代码更容易转换为底层的内容系统可以解释。如果不是这样,那么对于每个阵列访问,机器和程序员之间会有一个不必要的转换操作。 它使编译更容易。
引自论文:
答案 2 :(得分:26)
因为0是指向数组头部的指针到数组的第一个元素的距离。
考虑:
int foo[5] = {1,2,3,4,5};
要访问0,我们会这样做:
foo[0]
但是foo分解为指针,并且上面的访问具有类似的指针算术方式来访问它
*(foo + 0)
这些天指针算术不经常使用。回过头来的时候,这是一个方便的方法来获取一个地址并将X“整数”从该起点移开。当然,如果你想留在原地,你只需加0!
答案 3 :(得分:21)
因为基于0的索引允许......
array[index]
......实施为......
*(array + index)
如果索引是从1开始的,编译器需要生成:*(array + index - 1)
,这个“-1”会影响性能。
答案 4 :(得分:12)
因为它使编译器和链接器更简单(更容易编写)。
“...通过地址和偏移量引用内存直接在硬件上直接表示在所有计算机体系结构上,因此C中的这个设计细节使编译更容易”
和
“......这使得实施更简单......”
答案 5 :(得分:5)
出于同样的原因,当它是星期三,有人问你到星期三多少天,你说0而不是1,当它是星期三,有人问你到星期四多少天,你说1而不是2
答案 6 :(得分:5)
数组索引始终以零开头。假设基址为2000.现在arr[i] = *(arr+i)
。现在if i= 0
,这意味着*(2000+0
)等于数组中第一个元素的基址或地址。此索引被视为偏移量,因此默认索引从零开始。
答案 7 :(得分:2)
我读过的基于零的编号的最优雅的解释是观察到值不存储在数字行上的标记位置,而是存储在它们之间的空间中。第一项存储在0和1之间,下一项存储在1和2之间,等等。第N项存储在N-1和N之间。可以使用任一侧的数字来描述一系列项目。按照惯例,使用下面的数字描述单个项目。如果给出一个范围(X,Y),使用下面的数字识别单个数字意味着可以在不使用任何算术(它的项目X)的情况下识别第一个项目但是必须从Y中减去一个以识别最后一个项目(Y -1)。使用上面的数字识别项目可以更容易识别范围中的最后一项(它将是项目Y),但更难识别第一项(X + 1)。
虽然基于上面的数字来识别项目并不可怕,但是将范围(X,Y)中的第一项定义为X之上的项目通常比将其定义为下面的项目更好。 (X + 1)。
答案 8 :(得分:1)
技术原因可能源于指向数组内存位置的指针是数组第一个元素的内容。如果你声明指针的索引为1,那么程序通常会将一个值加到指针上,以访问当前不是你想要的内容。
答案 9 :(得分:1)
这是因为address
必须指向数组中右边的element
。让我们假设以下数组:
let arr = [10, 20, 40, 60];
现在让我们考虑地址的开头为12
,而地址element
的大小为4 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
如果不是不是 zero-based
,则从技术上讲,我们在array
中的第一个元素地址将是16
,这是错误的,因为其位置是{{1} }。
答案 10 :(得分:1)
我来自Java背景。我已经在下图中给出了这个问题的答案,下图是我写的一张纸,很容易说明
主要步骤:
注意:图像中显示的块是内存表示形式
答案 11 :(得分:1)
假设我们要创建一个大小为5的数组
int array [5] = [2,3,5,9,8]
让数组的第一个元素指向位置100
让我们考虑索引从1开始而不是从0开始。
现在我们必须借助索引找到第一个元素的位置
(记住第一个元素的位置是100)
因为整数的大小是4位
因此->考虑索引1的位置将是
索引(1)的大小*整数(4)的大小= 4
所以显示给我们的实际位置是
100 + 4 = 104
这是不正确的,因为初始位置在100。
它应该指向100而不是104
这是错误的
现在假设我们从0开始进行索引
然后
第一个元素的位置应为
索引(0)的大小*整数(4)的大小= 0
因此->
第一个元素的位置是100 + 0 = 100
那是元素的实际位置
这就是为什么索引从0开始;
我希望它能澄清您的观点。
答案 12 :(得分:1)
尝试使用基于1的矩阵上的X,Y坐标访问像素屏幕。这个公式非常复杂。为什么复杂?因为您最终将X,Y坐标转换为一个数字,即偏移量。为什么需要将X,Y转换为偏移量?因为这就是计算机内存的组织方式,作为连续的存储单元流(数组)。计算机如何处理阵列单元?使用偏移量(来自第一个单元格的位移,基于零的索引模型)。
因此,在您需要(或编译器需要)的代码中的某些时刻将1-base公式转换为基于0的公式,因为这就是计算机处理内存的方式。
答案 13 :(得分:0)
首先,您需要知道数组在内部被视为指针,因为“数组本身的名称包含数组的第一个元素的地址”
ex. int arr[2] = {5,4};
考虑数组从地址100开始 因此元素第一个元素将在地址100处,第二个元素将在104地址处 现在, 考虑如果数组索引从1开始,那么
arr[1]:-
这可以这样写在指针表达式中-
arr[1] = *(arr + 1 * (size of single element of array));
现在考虑int的大小为4个字节,
arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);
我们知道数组名称包含其第一个元素的地址,所以arr = 100 现在,
arr[1] = *(100 + 4);
arr[1] = *(104);
给出,
arr[1] = 4;
由于此表达式,我们无法访问地址100的元素,这是官方的第一个元素,
现在考虑数组索引从0开始,所以
arr[0]:-
这将解析为
arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);
现在,我们知道数组名称包含其第一个元素的地址 所以,
arr[0] = *(100);
给出正确的结果
arr[0] = 5;
因此,数组索引始终在c中从0开始。
参考:所有细节都写在“ Brian kerninghan和Dennis Ritchie的C编程语言”一书中
答案 14 :(得分:0)
在数组中,索引指示距起始元素的距离。因此,第一个元素与起始元素的距离为0。因此,这就是数组从0开始的原因。
答案 15 :(得分:-2)
数组名是一个指向基址的常量指针。当你使用arr [i]时,编译器将其操作为*(arr + i)。因为int范围是-128到127,编译器认为-128为-1是负数,0到128是正数。因此,数组索引总是从零开始。
答案 16 :(得分:-3)
当我们访问数组时,becoz编译器使用下面的公式 ((基地址)+索引*大小) fisrt元素总是存储在数组的基址中... 因此,如果我们从1开始,我们无法访问第一个元素,因为它给出了sesond元素的地址...... 所以它从0开始。