說明:int (*p)[4] 和 int *p[4](數組指針和指針數組),如果你是一個初學者,也許當你看到這兩個名詞的時候,已經懵了。其實,只要你理解了其中的含義.這兩個名詞對你來說會相當簡單並且很有趣,下面,我們就來深入探討一下究竟什么是數組指針,什么是指針數組。
一.指針數組
1.前面我們已經學過數組了,比如說要創建一個一維整型數組,該怎么創建呢?應該是這樣的:int arr[N];其中,arr是數組名,即變量名,N是你所創建的這個數組中的元素個數,而前面的int則是這些元素的類型。所以其實可以將它讀作整型變量數組。那萬一你所創建的數組元素不是整型和浮點型這些基本類型,而是一個指針類型呢?這就是指針數組了。
2.指針數組,首先它也是一個數組,只不過這個數組中的元素的類型為指針類型,舉個例子:double *arr[4],這是一個指針數組,包含四個元素,其中每個元素都是double*類型的,簡單來說,它就是一個用來存儲指針的數組。用一個圖來說明這個指針數組的內存布局:
3.既然指針數組是一個數組,那么它就應該有數組所應具有的一些特點。舉個例子,對於double* p[4],p+1加的是數組的步長,即一個double*的大小,四個字節(注意:在32位機中,所有指針的大小都為4個字節)。而如果對數組名p進行取地址后,則&p+1加的是sizeof(p),即4*4 = 16個字節,即&p+1就跨過了整個數組。
示例:
1 #include<stdio.h> 2 int main() 3 { 4 double *p[4] = {NULL}; 5 printf("p = %p\t",p); 6 printf("p+1 = %p\n",p+1); 7 printf("&p = %p\t",&p); 8 printf("&p+1 = %p\n",&p+1); 9 return 0; 10 }
程序運行結果:
二.數組指針
1.指針相信大家都比較熟悉了,比如:int *p定義了一個指針p,該指針指向一個整型數據單元,如果對該指針執行加1操作,則加的是4個字節;又如char *q定義了一個指針q,該指針指向一個字符型的數據單元,如果對該指針執行加1操作,則實際上加的是1(個字節)。那么問題來了,萬一要定義一個指針,它所指向的數據單元為一個一維數組怎么辦呢?對他執行加1操作又能得到什么呢?這就是數組指針了。
2.數組指針,首先得明白它是一個指針,只不過這個指針指向的數據單元為一個數組,舉個例子,現在有一個一維數組int arr[4];現在要定義一個數組指針來指向它,按照一般指針的理解,應該是這樣的,int[4]* p;表示定義一個指針p,而該指針的類型為int[4]*型的,但這在編譯器中是會報錯的,沒什么理由,語法規定。實際上對這個數組指針的定義應該是這樣的:int (*p)[4] = arr;說實話,這樣看着,筆者覺得挺別扭的,不過沒辦法,編譯器就只認這個寫法,不過這完全不影響我們按照第一種寫法去理解數組指針的本質。
3.上面已經說了,數組指針實質就是一個指針,只不過其指向的類型與基本類型不同罷了。對於基本類型的指針,執行加1加的是指針指向數據類型的字節數,那么對於數組指針呢?顯然加1加的也是指針指向數據類型的字節數,那么數組指針指向數據類型的大小怎么判斷呢?舉個例子:int(*p)[4],下面將通過一張內存數據圖對此進行闡述:
如圖:該指針里面存的是一個數組的首地址,只不過該指針的類型為int[4] *型,這就導致了該指針的步長為4*4 = 16個字節,所以對該指針執行加一操作,實際上加的是16個字節,即整個數組的大小。
1 #include<stdio.h> 2 int main() 3 { 4 int arr[4]; 5 int (*p)[4] = (int(*)[4])arr; 6 printf("%p\t",p); 7 printf("%p\n",p+1); 8 return 0; 9 }
程序運行結果:
4.數組指針與二維數組的關系是什么呢?首先要知道,二維數組 int arr[m][n] 可以想象成是具有m行,n列的一個數組矩陣,也可以想象成是有m個一維數組,其中每個一維數組里面又有n個int型的元素.那么是否可以用一個類型為int[n] *型的指針指向該二維數組來實現行間跳轉訪問呢?答案是肯定的!就拿上面例子來說,假如有一個二維數組int arr[m][n],則可以定義一個數組指針為:int (*p)[n] = arr(這里最好強轉一下),然后用p對數組進行訪問,由以上可講可知,這里的p+1加的是n*4個字節,即加的是二維數組每行的字節數。
示例:
1 #include<stdio.h> 2 int main() 3 { 4 int arr[3][4]; 5 int (*p)[4] = (int(*)[4])arr; 6 printf("%p\t",p); 7 printf("%p\n",p+1); 8 return 0; 9 } 10
程序運行結果:
注意:二維數組的存儲在內存中實際上是線性存儲的,可以說任何數據在內存上的存儲都是線性存儲的,但這並不影響我們用二維的思維去理解它。
三.下面是一個數組指針當做二維數組名訪問數組的示例,只是為了鞏固與拓展一下以上,對於二維數組名的具體使用方式,在下次更新(后天)會詳細介紹。這里就簡單介紹一下,當把二維數組名賦給一個指針數組后,例如如下示例,則該指針就擁有了二維數組名訪問二維數組的方式,比如在這里,p代表數組的首地址,由於其擁有了二維數組名的特性,則**p就是二維數組里的第一個元素,而*(*(p+i)+j)是二維數組第i行第j列的元素。
示例:
1 #include<stdio.h> 2 int main() 3 { 4 int arr[3][4]; 5 int count = 0; 6 for(int i = 0;i<3;i++) 7 for(int j = 0;j<4;j++) 8 arr[i][j] = count++; 9 for(int i = 0;i<3;i++) 10 { 11 for(int j = 0;j<4;j++) 12 printf("%2d ",arr[i][j]); 13 putchar(10); 14 } 15 int (*p)[4] = (int(*)[4])arr; 16 for(int i = 0;i<3;i++) 17 { 18 for(int j = 0;j<4;j++) 19 printf("%2d ",*(*(p+i)+j)); 20 putchar(10); 21 } 22 return 0; 23 }
程序運行結果: