前言
前面一篇說到了複雜指針的第一種指針——二重指針, 本篇博文接着來聊聊更加複雜的指針。
指針數組和數組指針
首先需要明確的是指針數組是數組,而數組指針是指針。 之所以把它們放在一起, 是因爲它們在形式上非常相似, 但實際上它們的用法和意義完全不同。 譬如定義一個指針數組: int *a[10], 表示一個有 10 個 int* 類型指針的數組; 定義一個數組指針: int (*pa)[10], 可以表示一個指向 int 類型、長度爲 10 的一維數組的指針。
注意: 上面提到的都是我們比較常見的指針數組和數組指針, 實際上還有許多指針數組和數組指針, 譬如: int **a[10]、 int (*pa)[10][10] 等等, 但是這些出現的頻率實在太低, 所以一般說到指針數組和數組指針時都特指上述兩類比較常見的。
指針數組和數組指針在內存中的表示
1.指針數組
從圖上看到內存中開闢了兩塊空間, 一塊用於存儲二重指針 pa, 一塊空間用於存儲指針數組。 並且二重指針 pa 指向了指針數組名 a。指針數組本質上和一維數組沒有區別, 唯一區別的是它們存儲的東西不同, 指針數組存儲的元素是指針。 圖中, 我特意在指針數組的下面標明瞭數組元素的假定地址, 這裏使用的模型是 LP64, 所以該指針數組裏面的元素都是 8 byte 大小的指針。 注意指針的大小與指針類型無關。
在指針篇(3)一文中, 我們提到了二重指針可以指向一重指針。 那麼爲什麼二重指針可以指向指針數組名呢? 我們知道一維數組的數組名在作右值時可以隱式轉換爲指針類型, 等效於數組首元素首地址。 同理指針數組名在作右值時也可以隱式轉換爲指針類型, 等效於數組首元素首地址。所以「pa = a 」等效於「pa = &a[0] 」。 指針數組的元素都是指針, 所以「pa = a 」是成立的。
2. 數組指針
int a[2][10] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,},
{10, 11, 12, 13, 14, 15, 16, 17, 18, 19,},
};
int *pa = NULL;
pa = &a[0][0];
int a[2][10] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,},
{10, 11, 12, 13, 14, 15, 16, 17, 18, 19,},
};
int (*pa)[10] = NULL;
pa = a;
區分指針數組和數組指針的技巧
很多人弄不清楚指針數組和數組指針這兩者到底哪個是指針, 哪個是數組。 所以我們首先說說如何在字面上區分這兩者。 其實在中文表達當中, 通常會把名詞(主語)放在後面, 修飾詞(定語)放在前面。 所以指針數組是數組在後, 指針在前, 重點表示的是數組。 反之, 數組指針它重點要表達的是一個指針。
那麼又如何區分具體的 C 語言代碼呢?這裏涉及到一個運算符優先級的問題。 C 語言中有一個運算符優先級表, 大多數情況下不需要用到這個優先級表。 不過作爲 C 語言的一個知識點, 也有必要會看懂優先級表和記住幾個比較重要運算符的優先級。 關於這一點會在下一篇博文中講解。 這裏我們只涉及到「*」(解引用運算符)、「()」、「[]」這三個運算符。
在優先級表中, 上述的幾個運算符的優先級順序爲:「[]」> 「()」 > 「*」。 所以當「*」和「[]」同時作用於 a 的時候, a 先與「[]」結合構成一個數組, 然後再與「*」結合, 結果構成一個指針數組, 其實指針數組也可以寫成 int *(a[10]),不過這裏的括號沒有起到任何作用, 因爲「[]」的優先級高於「*」; 同理數組指針中「()」就起到了一個隔離的作用, 這時候「[]」就不直接作用於 a, 所以 a 要先與「*」結合構成指針,然後再與「[]」結合。
2017 年 7 月 13 日
Kilento