C語言陣列指標和指標陣列

2020-07-16 10:04:27
在許多 C 程式中,指標常被用於參照陣列,或者作為陣列的元素。指向陣列的指標常被簡稱為陣列指標(array pointer),而具有指標型別元素的陣列則被稱為指標陣列(pointer array)。

陣列指標

為了便於舉例,下面的描述均以一個 int 陣列為例。同樣的原理可以應用於其他型別陣列,包括多維陣列。

要宣告指向陣列型別的指標,必須使用括號,如下所示:
int (* arrPtr)[10] = NULL;   // 一個指標,它指向一個有10個int元素的陣列

如果沒有括號,則宣告 int*arrPtr[l0];表示 arrPtr 是一個具有 10 個 int 型別指標的陣列。

在該例中,指向有 10 個 int 元素的陣列的指標會被初始化為 NULL。然而,如果把合適陣列的地址分配給它,那麼表示式 *arrPtr 會獲得陣列,並且(*arrPtr)[i] 會獲得索引值為 i 的陣列元素。根據下標運算子的規則,表示式(*arrPtr)[i] 等同於 *((*arrPtr)+i)。因此,**arrPtr 獲得陣列的第一個元素,其索引值為 0。

為了展示陣列指標 arrPtr 的幾個運算,下例使用它來定位一個二維陣列的某些元素,也就是矩陣內的某些行:
int matrix[3][10];            // 3行,10列的陣列
                                    // 陣列名稱是一個指向第一個元素的指標,也就是第一行的指標
arrPtr = matrix;            // 使得arrPtr指向矩陣的第一行
(*arrPtr)[0] = 5;       // 將5賦值給第一行的第一個元素
arrPtr[2][9] = 6;           // 將6賦值給最後一行的最後一個元素
++arrPtr;                   // 將指標移動到下一行
(*arrPtr)[0] = 7;           // 將7賦值給第二行的第一個元素

在初始化賦值後,arrPtr 指向矩陣的第一個行,正如矩陣名稱 matrix 一樣。在這種情況下,使用 arrPtr 獲取元素的方式與使用 matrix 完全一樣。例如,賦值運算(*arrPtr)[0]=5 等效於 arrPtr[0][0]=5 和 matrix[0][0]=5。

然而,與陣列名稱 matrix 不同的是,指標名稱 arrPtr 並不代表一個常數地址,如運算 ++arrPtr 所示,它進行了自增運算。這個自增運算會造成儲存在陣列指標的地址增加一個陣列空間大小,在本例中,即增加矩陣一行的空間大小,也就是 10 乘以 int 元素在記憶體中所佔位元組數量。

如果想把一個多維陣列傳入函數,則必須宣告對應的函數引數為陣列指標。最後要注意的是,如果 a 是一個具有 10 個 int 型別元素的陣列,那麼無法使用下面的方式對前面例子中的指標 arrPtr 賦值:
arrPtr = a;                  // 錯誤:指標型別不匹配

錯誤的原因是,陣列名字,例如上文的 a,會被隱式地轉換為指標,指向陣列第一個元素,而不是指向整個陣列。指向 int 的指標沒有被隱式地轉換為指向 int 陣列的指標。本例中的賦值操作需要顯式的型別轉換,在型別轉換運算子中明確指定目標型別是
int (*) [10]:
arrPtr = (int (*)[10])a;        // 合法

在前文 arrPtr 的宣告語句(int(*arrPtr)[10]=NULL;)中,刪除其中識別符號 arrPtr,就可得到 int(*)[10],即對應的陣列指標型別。然而,為了提高可讀性和靈活性,可以利用 typedef 為所用的型別定義一個簡單的名字:
typedef int ARRAY_t[10];    // 定義一個“具有10個元素陣列”型別名稱
ARRAY_t  a,                     // 具有該型別的陣列
         *arrPtr;               // 一個指向該陣列型別的指標
arrPtr = (ARRAY_t *)a;  // 使得arrPtr指向a

指標陣列

指標陣列(也就是元素為指標型別的陣列)常常作為二維陣列的一種便捷替代方式。一般情況下,這種陣列中的指標會指向動態分配的記憶體區域。

例如,如果需要處理字串,可以將它們儲存在一個二維陣列中,該陣列行空間大小必須足以儲存下可能出現的最長字串:
#define ARRAY_LEN 100
#define STRLEN_MAX 256
char myStrings[ARRAY_LEN][STRLEN_MAX] =
{ // 墨菲定律的幾條推論:
“會出錯的事,總會出錯。”
“世上沒有絕對正確的事情。”
“每個解決辦法都會衍生出新的問題。”
};

然而,這個方式造成記憶體浪費,25600 位元組中只有一小部分被實際使用到。一方面,短字串會讓大部分的行是空的;另一個方面,有些行根本沒有用到,但卻得為它預留記憶體。

一個簡單的解決方案是,使用指標陣列,讓指標指向物件(在此處的物件就是字串),然後只給實際存在的物件分配記憶體(未用到的陣列元素則是空指標)。
#define ARRAY_LEN 100
char *myStrPtr[ARRAY_LEN] =        // char指標的陣列
{ // 墨菲定律的幾條推論:
“會出錯的事,總會出錯。”
“世上沒有絕對正確的事情。”
“每個解決辦法都會衍生出新的問題。”
};

圖 1 展示了物件在記憶體中的儲存情況:


圖 1