为什么数组中的索引从C中的零开始而不是1?
为什么数组中的索引从C中的零开始而不是1?
Answers:
在C语言中,数组的名称本质上是一个指针[但是请参见注释],它是对内存位置的引用,因此表达式表示的array[n]
是n
远离起始元素的内存位置元素。这意味着将索引用作偏移量。数组的第一个元素正好包含在数组所指的存储位置中(相距0个元素),因此应将其表示为array[0]
。
有关更多信息:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
sizeof arr
产生数组对象的大小,而不是指针的大小。
sizeof
符或一元运算&
符的操作数,或者是用于初始化数组的字符串文字,否则其类型为” array of type“将转换为类型为” pointer to type“的表达式,该表达式指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则行为未定义。 ”
这个问题是一年多以前发布的,但是这里...
虽然Dijkstra的文章(以前在现在已删除的答案中引用过)从数学角度讲是有意义的,但在编程方面却不那么相关。
语言规范和编译器设计者所做的决定基于计算机系统设计者作出的从0开始计数的决定。
丹尼·科恩(Danny Cohen)摘自《和平呼吁》。
对于任何基数b,仅当编号从0开始时,才 由正好N个数字(包括前导零)表示前b ^ N个非负整数。
这可以很容易地进行测试。在以2为底2^3 = 8
的第8个数字是:
111
可以使用3
位表示,而1000
需要额外的位(4位)。
计算机存储器地址具有2^N
按N
位寻址的单元。现在,如果我们从1开始计数,则2^N
单元格将需要N+1
地址线。需要额外位才能访问1个地址。(1000
以上情况)。解决该问题的另一种方法是使最后一个地址不可访问,并使用N
地址线。
与从0开始计数相比,这都是次优的解决方案,这将使使用精确的N
地址线可访问所有地址!
0
从此开始计算的决定已经渗透到所有数字系统中,包括在其上运行的软件,因为它使代码转换为底层系统可以解释的内容更加容易。如果不是这样,则对于每次数组访问,机器和编程器之间都将进行一次不必要的转换操作。它使编译更加容易。
引用本文:
a[b]
与*(a+b)
早期编译器一样实现。即使在今天,您仍然可以2[a]
代替a[2]
。现在,如果索引不是从0开始,a[b]
则将变为*(a+b-1)
。这将需要在该时间的CPU上加2而不是0,这意味着速度的一半。显然是不可取的。
由于基于0的索引允许...
array[index]
...实施为...
*(array + index)
如果索引是基于1的,则编译器将需要生成:*(array + index - 1)
,并且此“ -1”将损害性能。
因为它使编译器和链接器更简单(更易于编写)。
参考:
“ ...在几乎所有计算机体系结构上,都直接在硬件中表示通过地址和偏移量引用的内存,因此C中的这种设计细节使编译更加容易”
和
“ ...这使实现更简单...”
数组索引始终以零开始。假设基址为2000 arr[i] = *(arr+i)
。现在if i= 0
,这意味着*(2000+0
)等于基地址或数组中第一个元素的地址。该索引被视为偏移量,因此bydeafault索引从零开始。
出于同样的原因,当是星期三,有人问您到星期三有多少天时,您说的是0而不是1;而当是星期三,有人问您直到星期四有多少天时,您说的是1而不是2。
对于基于零的编号,我读过的最优雅的解释是,观察值不是存储在数字行的标记位置,而是存储在它们之间的空格中。第一项存储在零到一个之间,第二项存储在零到两个之间,依此类推。第N个项目存储在N-1和N之间。可以使用任一侧的数字来描述一系列项目。按照惯例,使用下面的数字描述各个项目。如果给定一个范围(X,Y),则使用下面的数字标识单个数字意味着无需使用任何算术运算符(它是X项)就可以标识第一个项目,但是必须从Y中减去一个来标识最后一个项目(Y -1)。使用上面的数字识别项目将使识别范围中的最后一个项目(即项目Y)更加容易,
尽管根据上面的数字来标识项目并不可怕,但是将范围(X,Y)中的第一个项目定义为X之上的项目通常比定义为以下的项目(X + 1)。
尝试在基于1的矩阵上使用X,Y坐标访问像素屏幕。该公式非常复杂。为什么复杂?因为最终将X,Y坐标转换为一个数字,即偏移量。为什么需要将X,Y转换为偏移量?因为这就是内存在计算机内部的组织方式,所以它们是连续的存储单元(阵列)流。计算机如何处理阵列单元?使用偏移量(从第一个单元格开始的位移,从零开始的索引模型)。
因此,在代码中的某些时候,您需要(或编译器需要)将基于1的公式转换为基于0的公式,因为这是计算机处理内存的方式。
假设我们要创建一个大小为5的数组
int array [5] = [2,3,5,9,8]
让该数组的第一个元素指向位置100
,让我们考虑从1开始而不是从索引开始0。
现在我们必须借助索引找到第一个元素
的位置(记住第一个元素的位置是100),
因为整数的大小是4位,
因此->考虑索引1的位置将是
size (1)的整数*整数(4)的大小= 4,
因此它将向我们显示的实际位置是
100 + 4 = 104
这是不正确的,因为初始位置在100。
它应该指向100而不是在104。
这是错误的。
现在假设我们从0开始进行索引,
那么
第一个元素的位置应该是
index(0)*整数的大小(4)= 0
因此->
第一个元素的位置是100 + 0 = 100
,这就是元素的实际位置,
这就是为什么索引从0开始的原因;
我希望它能澄清您的观点。
首先,您需要知道数组在内部被视为指针,因为“数组本身的名称包含数组第一个元素的地址”
ex. int arr[2] = {5,4};
考虑到数组从地址100开始,因此元素第一个元素将位于地址100,第二个元素现在位于地址104,请考虑如果数组索引从1开始,则
arr[1]:-
可以这样写在指针表达式中
arr[1] = *(arr + 1 * (size of single element of array));
考虑int的大小是4bytes,现在,
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编程语言”一书中
在数组中,索引指示距起始元素的距离。因此,第一个元素与起始元素的距离为0。因此,这就是数组从0开始的原因。
这是因为address
必须指向element
数组中的右侧。让我们假设以下数组:
let arr = [10, 20, 40, 60];
现在让我们考虑地址be 的开始12
和element
be 的大小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
是错误的,因为它的位置是12
。