(3)指標和陣列
在c中指標和陣列似乎有著千絲萬縷的關係。其實它們不是一回事:指標是指標,陣列是陣列,兩者不相同。
說它們有關係,不過是因為常見這樣的**:
int main()
; int n = sizeof(array) / sizeof(int);
int *p = array;
int i;
for (i = 0; i < n; i++)
printf("p[%d]...%d\n", i, p[i]);
system("pause");
return 0;
}
執行
在上面的**中,指標和下標運算子的結合使用,給人一種指標和陣列是一樣的感覺。
本質是:陣列名是乙個指向陣列起始元素的常量指標。這也是陣列和指標的唯一聯絡!
之所以可以使用 p[i] 來訪問陣列中的元素,是因為在編譯器中 p[i] 被解釋為 *(p+i),這仍然是指標的功能。對編譯器而言,用p[i]表達*(p+i)的含義是沒有意義的,它只是為了讓人看著舒服,用著方便。這是語法糖:
p[i]是*(p+i)的簡單寫法,實際上,至少對於編譯器來說,這樣的運算子完全可以不存在。
可是對於人類來說,*(p+i)的寫法在解讀上比較困難,寫起來也麻煩(鍵入量大)。因此,c語言引入運算子。
就像這樣,這些僅僅是為了讓人類容易理解而引入的功能,的確可以讓我們感受到程式語言的甜蜜味道(容易著手),有時我們稱這些功能為語法糖(syntax sugar 或者 syntactic sugar)。
以上摘自《征服c指標》,藉此推薦這本書。書中一針見血地指出:只有在宣告語句中,才表達陣列的含義,在表示式中,與陣列無關!
總結起來就是,看似陣列的用法:p[i],其實是*(p+i)的語法糖,p仍然是指標,與陣列並無關係。
指標和陣列的不同之處,還可以從下面的例子看出
void fun(int array[5])
int main()
; printf(" sizeof(array)...%d\n", sizeof(array));
fun(array);
return 0;
}
執行
從執行結果看,函式形參雖然用陣列的方式進行了宣告,但仍然被當做指標。這揭示了c語言中傳遞陣列時的規則:傳遞過去的是位址,是指向陣列起始元素的位址。之所以這樣,基於兩點;
從效率上考慮,若是把整個陣列賦值過去,太耗時,也耗空間。還不如傳位址過去,使用同乙份內容。
在c語言設計之初,賦值操作就僅限於基本型別(char、int、float……),而陣列是聚合型別。
這給我們的程式設計啟示是:傳遞陣列時,不要忘了把陣列大小也傳遞過去。否則,函式那邊由於不知道陣列大小,極易越界。應這樣設計函式 void fun(int *array, int n),n是陣列大小。
還有一點需要指出,即使函式被設計成void fun(int array[5], int n),array依然被看成是指標。也就是說即使陣列帶了長度,該長度也會被編譯器忽略掉。一句話:形參中的陣列統統看成指標。
既然如此,還不如直接寫成void fun(int *array, int n)。指標的形式,更能表達本意。
再思考:如果p[i]是*(p+i)的意思,由於加法具有交換律:p+i=i+p,那麼i[p]同樣可以表達p[i]的意思,是這樣的嗎?實驗驗證:
int main()
; int n = sizeof(array) / sizeof(int);
int *p = array;
int i;
for (i = 0; i < n; i++)
printf(" %d[p]...%d\n", i, i[p]);
return 0;
}
執行
實驗證明,我們的猜想是正確的:p[i]確實是*(p+i)的語法糖。i[p]這樣的寫法是否很逆天呢!
總結:只有在函式形參中,僅有這一種情況,宣告的陣列,如 int array會被看作是指標。其它情況下,指標與陣列並無聯絡。
&array的含義
還有一點,對於 int array[5];array表示指向陣列起始元素的指標,那麼&array又是什麼呢?實驗下:
int main()
; printf(" array...%p\n", array);
printf(" &array...%p\n", &array);
printf("&array+1...%p\n", &array+1);
return 0;
}
執行
分析實驗結果:0031fcec與0031fce4相差8,而sizeof(array)就是8。
結論就是:array和&array都是指標,但型別不同。array的型別是int*,而&array的型別是int(*)[2]。array是指向普通int型別的指標;&array是陣列指標,該陣列元素是int型別的,且陣列大小是2。
至於array和&array兩者的值是一樣的,應該很好理解。
補充 標量(scalar):簡單講,標量就是指char、int、double和列舉型別等數值型別,再加上指標。
至於陣列、結構體和共用體這樣將多個標量進行組合的型別,我們稱之為聚合型別(aggregate)。
那麼為什麼int(*)[2]表示的是陣列指標呢?這需要透徹理解c的宣告語法。又比如,二維陣列(更甚者,多維陣列)的陣列名又是什麼型別的指標呢?這需要了解c中陣列的實際含義,後序講解。
更多指標和陣列的內容見(5)c陣列本質。
專欄目錄:
娓娓道來c指標 3 指標和陣列
3 指標和陣列 在c中指標和陣列似乎有著千絲萬縷的關係。其實它們不是一回事 指標是指標,陣列是陣列,兩者不相同。說它們有關係,不過是因為常見這樣的 cpp view plain copy print int main int n sizeof array sizeof int int p array...
娓娓道來c指標 8 開發可變引數函式
8 開發可變引數函式 在c語言中,可變引數的函式極其常見,如常用的printf 可變引數函式的一般形式如下 返回值型別 函式名 型別1 引數1,型別2 引數2,型別n 引數n,如上所示,這是乙個典型的可變引數樣式,它共有n個確定的引數,最後的.表示可變引數的含義。必須指出.必須位於最後,並且它至少要...
娓娓道來c指標 4 解析c的宣告語句
4 解析c的宣告語句 在繼續探索c指標之前,有必要來解析下c語言中複雜的宣告語法。只需要記住兩則 乙個原則,乙個規則。原則 先看標示符。規則 運算子優先順序是規則。舉例說明 1.最簡單的 int array 3 結論 array是陣列,陣列規模是3,元素型別是int。解析過程 先看標示符 array...