為什么數(shù)組的下標從 0 開始?
示例代碼
因為現(xiàn)代化的高級編程語言,基本只能使用 array[index]
來操作數(shù)組指定的索引,所以這里使用 C 語言,展示一下更 “原始的方法” 操作數(shù)組的魅力。
#include <stdio.h>
int main(void) {
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++) {
// *(arr + i) 相當于 arr[i]
// (arr + i) 是第 i 個元素的地址
printf("Element %d: value = %d, address = %p\n",
i,
*(arr + i),
(void *)(arr + i));
}
return0;
}
// 你的輸出地址和這里的不一樣,屬于正常現(xiàn)象
Element 0: value = 1, address = 0x16d65adc0
Element 1: value = 2, address = 0x16d65adc4
Element 2: value = 3, address = 0x16d65adc8
Element 3: value = 4, address = 0x16d65adcc
Element 4: value = 5, address = 0x16d65add0
歷史原因
這應(yīng)該是主要原因。
計算機較為早期的編程語言,例如 BCPL, 使用 0 作為數(shù)組開始的下標,然后 C 語言沿襲了這個設(shè)計來簡化編譯過程,再來后來的高級語言 (Java, Python) 等為了兼顧歷史,全部延續(xù)了 C 語言的慣例,所以最終也就變成了一個約定俗成的特性。
性能優(yōu)化
這個優(yōu)化主要體現(xiàn)在計算機內(nèi)存尋址效率上,因為數(shù)組的訪問依賴于內(nèi)存地址的計算,具體來說,如果使用變量 base 來表示數(shù)組的地址:
(1) 當下標從 0 開始時:
- arr[0] 就表示數(shù)組首個元素的地址
- arr[k] 就表示從數(shù)組首元素開始,偏移 k 個元素的地址,偏移量為數(shù)據(jù)類型 typeSize 的大小
(2) 但是當下標從 1 開始時:
- arr[1] 就表示數(shù)組首個元素的地址
- arr[k] 就表示從數(shù)組首元素開始,偏移 k - 1 個元素的地址,偏移量為數(shù)據(jù)類型 typeSize 的大小為 4
通過對比兩個尋址公式可以看到,如果數(shù)組下標從 1 開始,每次隨機訪問數(shù)組元素時,都會增加 1 個額外的減法運算,也就是多了 1 條 CPU 減法指令,高頻訪問場景下會造成略微的性能差異。
內(nèi)存模型
數(shù)組在計算機內(nèi)存中實現(xiàn)為連續(xù)存儲,首地址對應(yīng)下標 0,具體元素的下標,就可以直接表示為相對首地址的偏移量。這樣,可以和物理內(nèi)存地址從 0 開始編號,保持相同的邏輯抽象語義。
數(shù)學邏輯與半開區(qū)間優(yōu)勢
在數(shù)學中,序列索引常從 0 開始(如離散數(shù)學中的 0 到 n-1 定義)。編程語言中,半開區(qū)間( 例如 Python 的切片 arr[0:n] 表示前 n 個元素)比閉區(qū)間的表示方式更簡潔。如果下標從 1 開始,表示方式就變?yōu)?nbsp;arr[1:n+1],增加不必要的復(fù)雜度。