數(shù)組兩個特性
為什么很多編程語言中數(shù)組都是從 0
開始編號赵抢,首先先了解一下數(shù)組的概念。
數(shù)組 Array
是一種線性表數(shù)據(jù)結(jié)構(gòu)功炮,是一組連續(xù)的內(nèi)存空間慕匠,用來存儲一組具有相同類型的數(shù)據(jù)。數(shù)組具備以下特性:
線性表阅仔,是數(shù)據(jù)排列成像一條線一樣的結(jié)構(gòu)吹散,每個線性表上的數(shù)據(jù)最多只有前和后兩個方向。比如八酒,除了數(shù)組送浊,還有鏈表、隊列和棧丘跌。
連續(xù)的內(nèi)存空間和相同類型的數(shù)據(jù)袭景,正因為這兩個特點,才讓數(shù)組的操作變得非常低效闭树,要想在數(shù)組中刪除耸棒、插入一個數(shù)據(jù),為了保證連續(xù)性报辱,就需要做大量的數(shù)據(jù)搬移工作与殃。
正因為這兩個特點,可以非常高效的通過下標(biāo)進(jìn)行隨機訪問碍现。
順序訪問和隨機訪問的區(qū)別
順序訪問:鏈表在內(nèi)存中不是按順序存放的幅疼,而是通過指針連在一起,為了訪問某一元素昼接,必須從鏈頭開始順著指針才能找到某一個元素爽篷。
隨機訪問:數(shù)組在內(nèi)存中是按順序存放的,可以通過下標(biāo)直接定位到某一個元素存放的位置慢睡。
尋址公式
一維數(shù)組尋址公式:
a[k]_address = base_address + k * type_size
二維數(shù)組尋址公式:
假設(shè)二維數(shù)組大小為 m*n
逐工,那么尋址公式為:
a[i][j]_address = base_address + (i * n + j) * type_size
三維數(shù)組尋址公式:
假設(shè)是 m*n*q
铡溪,那么尋址公式為:
a[i][j][k]_address=base_address + (i * n * q + j * q + k) * type_size
其中, type_size
表示數(shù)組中每個元素的大小泪喊。
驗證
例如棕硫,聲明一個長度為 10
的 int
類型的數(shù)組。
int arr[10] = { 0 };
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
運行結(jié)果如下袒啼,
從運行結(jié)果可以看出哈扮,計算機給數(shù)組 arr
,分配了 40
字節(jié)的內(nèi)存蚓再,首地址為 0x7ffeefbff4f0
灶泵,arr[0]
地址為:0x7ffeefbff4f0
,arr[9]
地址為:0x7ffeefbff514
对途,每個 int
有 4
個字節(jié)赦邻,故 arr[9]
結(jié)尾為 0x7ffeefbff514
。
在 C
語言中數(shù)組名代表首地址实檀,即第一個元素的地址惶洲,a[0]
就是偏移為 0
的位置,a[k]
就表示偏移 k
個元素類型大小的位置膳犹。得出計算公式:
a[k]_address = base_address + k * type_size
結(jié)論
CPU
性能層面考慮:從數(shù)組存儲的內(nèi)存模型上來看恬吕,“下標(biāo)”最確切的定義應(yīng)該是offset
。前面也講到须床,如果用 a
來表示數(shù)組的首地址铐料,a[0]
就是偏移為 0
的位置即首地址,a[k]
就表示偏移 k
個 type_size
的位置豺旬。如果數(shù)組編號從 1
開始計數(shù)钠惩,那這個公式就會變?yōu)椋?/p>
a[k]_address = base_address + k * type_size
a[k]_address = base_address + (k-1) * type_size
對比兩個公式,如果從 1
開始編號族阅,每次隨機訪問數(shù)組元素就多了一次減法運算篓跛,對于 CPU
來說就是多了一次減法指令,增加了性能開銷坦刀。
歷史原因?qū)用婵紤]:C
語言設(shè)計者用 0
開始計數(shù)數(shù)組下標(biāo)愧沟,之后的 Java
、JavaScript
等高級語言都效仿了 C
語言鲤遥,或者說沐寺,為了在一定程度上減少 C
語言程序員學(xué)習(xí) Java
的學(xué)習(xí)成本,因此繼續(xù)沿用了從 0
開始計數(shù)的習(xí)慣盖奈。實際上混坞,很多語言中數(shù)組也并不是從 0
開始計數(shù)的,比如 Matlab
卜朗。甚至還有一些語言支持負(fù)數(shù)下標(biāo)拔第,比如 Python
咕村。