陣列

為什麼我們需要陣列?

陣列提供了一種將物件組織成具有其自身重要性的聚合的方法。例如,C 字串是字元陣列(chars),字串如 Hello World!。具有作為一個單獨的字元不固有的聚合的含義。類似地,陣列通常用於表示數學向量和矩陣,以及多種型別的列表。此外,如果沒有某種方法對元素進行分組,則需要單獨處理每個元素,例如通過單獨的變數。它不僅不實用,而且不容易適應不同長度的集合。

在大多數上下文中,陣列被隱式轉換為指標

除了作為 sizeof 運算子,_Alignof 運算子(C2011)或一元 &(取址)運算子的運算元出現,或者作為用於初始化(其他)陣列的字串文字時,陣列被隱式轉換為指向其第一個元素的指標。這種隱式轉換與陣列下標運算子([])的定義緊密耦合:表示式 arr[idx] 被定義為等同於*(arr + idx)。此外,由於指標算術是可交換的,*(arr + idx) 也相當於*(idx + arr),而*(idx + arr) 又相當於 toidx[arr]。只要 idxarr 是一個指標(或一個衰減到指標的陣列),另一個是整數,所有這些表示式都是有效的並且計算得到相同的值。

作為一個特例,觀察 &(arr[0]) 相當於 &*(arr + 0),這簡化為 arr。所有這些表示式在最後衰落到指標的任何地方都是可以互換的。這再次簡單地表達了一個陣列衰減到指向其第一個元素的指標。

相反,如果取址運算子應用於 T[N] 型別的陣列( &arr),那麼結果的型別為 T (*)[N] 並指向整個陣列。這與至少針對指標算術的指向第一陣列元素的指標不同,指標算術是根據指向型別的大小來定義的。

函式引數不是陣列

void foo(int a[], int n);
void foo(int *a, int n);

雖然 foo 的第一個宣告對引數 a 使用類似陣列的語法,但這種語法用於宣告一個函式引數,宣告該引數作為指向陣列元素型別的指標。因此,foo() 的第二個簽名在語義上與第一個簽名相同。這對應於陣列值到指標的衰減,它們作為函式呼叫的引數出現,這樣如果變數和函式引數宣告為具有相同的陣列型別,那麼該變數的值適合在函式呼叫中使用,因為與引數關聯的引數。