娓娓道來c指標 (3)指標和數組,娓娓道來指標
(3)指標和數組
在c中指標和數組似乎有著千絲萬縷的關係。其實它們不是一回事:指標是指標,數組是數組,兩者不相同。
說它們有關係,不過是因為常見這樣的代碼:
int main(){int array[] = {1,2,3,4,5};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]){printf(" sizeof(array)...%d\n", sizeof(array));}int main(){int array[] = { 1, 2, 3, 4, 5 };printf(" sizeof(array)...%d\n", sizeof(array));fun(array);return 0;}運行
從運行結果看,函數形參雖然用數組的方式進行了聲明,但仍然被當做指標。這揭示了c語言中傳遞數組時的規則:傳遞過去的是地址,是指向數組起始元素的地址。之所以這樣,基於兩點;
這給我們的編程啟示是:傳遞數組時,不要忘了把數組大小也傳遞過去。否則,函數那邊由於不知道數組大小,極易越界。應這樣設計函數 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 array[] = { 1, 2, 3, 4, 5 };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(){int array[] = { 1, 2};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中數組的實際含義,後序講解。
專欄目錄:C指標
C語言的行指標的定義 int (*p)[3]; 與字元型數組指標數組的定義 int *p[3]有什不同,我有點模糊
int (*p)[3] = {0, 1, 2}
(int*) (int) (int) (int)
int* p[3] = {0, 1, 2}
(int) (int*) (int*) (int*)
C指標數組的問題,指向指標的指標指向指標數組
/*
pa[0]:0012FF24 pa[1]:0012FF30 pa[2]:0012FF3C
pp[0]:0012FF24 pp[1]:0012FF30 pp[2]:0012FF3C
pp[3]:00000001 pp[4]:00000002 pp[5]:00000003
pp[6]:00000004 pp[7]:00000005 pp[8]:00000006
pp[0][0]:1 pp[0][1]:2 pp[0][2]: 3
pp[1][0]:4 pp[1][1]:5 pp[1][2]: 6
pp[2][0]:7 pp[2][1]:8 pp[2][2]: 9
Press any key to continue
*/
#include <stdio.h>
void main() {
int a[3][3] = {1,2,3,4,5,6,7,8,9};
int *pa[3] = {a[0], a[0] + 3, a[0] + 6};
int **pp = pa;
printf("\n");
printf("pa[0]:%08X pa[1]:%08X pa[2]:%08X\n",pa[0],pa[1],pa[2]);
printf("pp[0]:%08X pp[1]:%08X pp[2]:%08X\n",pp[0],pp[1],pp[2]);
printf("pp[3]:%08X pp[4]:%08X pp[5]:%08X\n",pp[3],pp[4],pp[5]);
printf("pp[6]:%08X pp[7]:%08X pp[8]:%08X\n",pp[6],pp[7],pp[8]);
printf("\n");
printf("pp[0][0]:%d pp[0][1]:%d pp[0][2]: %d\n",pp[0][0],pp[0][1],pp[0][2]);
printf("pp[1][0]:%d pp[1][1]:%d pp[1][2]: %d\n",pp[1][0],pp[1][1],pp[1][2]);
printf("pp[2][0]:%d pp[2][1]:%d pp[2][2]: %d\n",pp[2][0],pp[2][1],pp[2][2]);
printf("\n");
}
/*
注意到: int *pa[3] = {a[0], a[0] + 3, a[0] + 6}; 不是 int *pa[3] = {a[0], a[0] + 1, a[0] + 2};
printf("pa[0]:%08X pa[1]:%08X pa[2]:%08X\n",pa[0],pa[1],pa[2]); // 輸出三個指標的地址
printf("pp[0]:%08X pp[1]:%08X pp[2]:%08X\n",pp[0],pp[1],pp[2]); // 與上一句相同
printf("pp[3]:%08X pp[4]:%08X pp[5]......餘下全文>>