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