關於指針數組與數組指針詳解(知識點全面)

關於指針數組與數組指針詳解(知識點全面)_利刃Cc的博客-CSDN博客_指針數組的作用

1.指針數組

如果一個數組中的所有元素保存的都是指針,那麼我們就稱它爲指針數組。其一般形式爲:

        數據類型    *數組名[常量表達式][常量表達式]...... ;

它是一個數組,數組的元素都是指針,數組佔多少個字節由數組本身的大小決定,每個元素都是一個指針。

 例如:char *arr[]={“Sunday”,“Monday”},存儲了兩個指針,第一個指針指向了字符串"Sunday",第二個指針指向了字符串"Monday",而sizeof(arr)=8,因爲在32位平臺,指針類型大小佔4個字節。指針數組最重要的用途是對多個字符串進行處理操作,因爲字符指針比二維數組更快更有效。

 

下面是個簡單的例子

  1. #include <stdio.h>
  2. int main()
  3. {
  4. //定義三個整型數組
  5. int a[5] = { 1,2,3,4,5 };
  6. int b[5] = { 6,4,8,3,1 };
  7. int c[5] = { 2,5,8,6,1 };
  8. //定義一個存放指向整型變量的指針的數組arr
  9. int* arr[] = { a,b,c };
  10. //通過接引用打印出三個一維數組的元素
  11. for (int i = 0; i < 3; i++)
  12. {
  13. for (int j = 0; j < 5; j++)
  14. {
  15. printf("%d ", *(arr[i]+j));
  16. }
  17. printf("\n");
  18. }
  19. return 0;
  20. }

 結果如下:

      1 2 3 4 5

      6 4 8 3 1

      2 5 8 6 1

以上對arr解引用的方式有很多,它們都是等價的,我們來舉個例子:

  1. #include<stdio.h>
  2. int main()
  3. {
  4. int i = 0;
  5. int a[3][4] = { {1,2,3,4} ,{5,6,7,8} ,{9,10,11,12} };//定義一個二維數組
  6. int* pa[3];//定義一個指針數組
  7. for (i = 0; i < 3; i++)//給指針數組賦值
  8. pa[i] = a[i];
  9. printf("指針數組的內容爲:\n");
  10. for (i = 0; i < 3; i++)//打印出指針數組的內容
  11. {
  12. int j;
  13. for (j = 0; j < 4; j++)
  14. printf("%d ", *(*(pa + i) + j));
  15. printf("\n");
  16. }
  17. //以下均爲不同方式的解引用操作
  18. printf("不同解引用操作的結果爲:\n");
  19. printf("%d,%d\n", a[1][1], *(pa[1] + 1));
  20. printf("%d,%d\n", a[1][1], *(*(pa+1) + 1));
  21. printf("%d,%d\n", a[1][1], (*(pa + 1))[1]);
  22. printf("%d,%d\n", a[1][1], pa[1][1]);
  23. return 0;
  24. }

結果如下所示: 

指針數組的內容爲:

1 2 3 4
5 6 7 8
9 10 11 12

不同解引用操作的結果爲:

6,6
6,6
6,6
6,6

從以上例子可看出解引用有多種方式,它們的等價形式如下:

*( pa[i] + j )         //等價於 *( a[i] + j )

*( *(p+i) + j )        //等價於 *( *(a+j) + j )

( *(p+i) )[ j ]        //等價於( *(a+i) )[ j ]

p[ i ][ j ]                //等價於 a[i][j]

---------------------------------------------------------------------------------------------------------------------------------

補充(1):指針數組還可以和字符串數組相結合使用,請看下面的例子:

  1. #include <stdio.h>
  2. int main(){
  3. char *str[3] = {"lirendada","C語言","C Language"};
  4.  
  5. printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
  6. return 0;
  7. }

結果如下:

lirendada

c語言

C Language

需要注意的是,字符數組 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位於其他的內存區域,和字符數組是分開的。

也只有當指針數組中每個元素的類型都是char *時,才能像上面那樣給指針數組賦值,其他類型不行。

 爲了便於理解,可以將上面的字符串數組改成下面的形式,它們都是等價的。

  1. #include <stdio.h>
  2. int main(){
  3. char *str0 = "lirendada";
  4. char *str1 = "C語言";
  5. char *str2 = "C Language";
  6. char *str[3] = {str0, str1, str2};
  7. printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
  8. return 0;
  9. }

補充(2):二維數組與指針數組的區別

  1. char *p1[]={"lirendada","C","C++"};
  2. char p2[][8]={"liren","C","C++"};

*p1,*(p1+1),*(p1+2):所指向的字符串常量是不規則長度的,且sizeof(p1)=12。


p2[0],p2[1],p2[2]所指向的字符串都是一定長度的,且sizeof(p2)=24。


插播:大家閱讀了頭是不是有點大呢哈哈,點個贊或者三連支持一下作者,說不定腦瓜子突然就懂了呢!!!


 

 2.數組指針

注:因爲數組指針對於一維數組的使用比較尷尬,對於一維數組,建議使用指針數組比較方便,這裏只涉及到關於二維數組與數組指針的知識!!!

首先引入二維數組的定義:二維數組在概念上是二維的,有行有列,但在內存中所有的元素都是連續排列的,以下面的二維數組爲例:

int a[3][4]={undefined{1,2,3,4},{5,6,7,8},{9,10,11,12}};

從概念上理解,a的分佈就像一個矩陣:

1        2        3        4

5        6        7        8

9       10      11      12

 從內存上理解,整個數組佔用一塊連續的內存:

 C語言中的二維數組是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最後存放 a[2] 行;每行中的 4 個元素也是依次存放。數組 a 爲 int 類型,每個元素佔用 4 個字節,整個數組共佔用 4×(3×4) = 48 個字節。

C語言允許把一個二維數組分解成多個一維數組來處理。對於數組 a,它可以分解成三個一維數組,即 a[0]、a[1]、a[2]。每一個一維數組又包含了 4 個元素,例如 arr[0] 包含 a[0][0]、a[0][1]、a[0][2]、a[0][3]。

 

假設數組a中第0個元素的地址爲1000,那麼每個一維數組的首地址如下圖所示:

 

 爲了更好的理解指針和二維數組的關係,我們先來定義一個指向 a 的指針變量 p:

int (*p)[4] = a ; 

括號中的*表明 p 是一個指針,它指向一個數組,數組的類型爲int [4],這正是 a 所包含的每個一維數組的類型。

[]的優先級高於*()是必須要加的,如果赤裸裸地寫作int *p[4],那麼應該理解爲int *(p[4]),p 就成了一個指針數組,而不是二維數組指針。

對指針進行加法(減法)運算時,它前進(後退)的步長與它指向的數據類型有關,p 指向的數據類型是int [4],那麼p+1就前進 4×4 = 16 個字節,p-1就後退 16 個字節,這正好是數組 a 所包含的每個一維數組的長度。也就是說,p+1會使得指針指向二維數組的下一行,p-1會使得指針指向數組的上一行。數組名 a 在表達式中也會被轉換爲和 p 等價的指針!

概念圖如以下所示:

 

 下面我們就來探索一下如何使用指針 p 來訪問二維數組中的每個元素。按照上面的定義:


1) p指向數組 a 的開頭,也即第 0 行;p+1前進一行,指向第 1 行。


2) *(p+1)表示取地址上的數據,也就是整個第 1 行數據。注意是一行數據,是多個數據,不是第 1 行中的第 0 個元素,下面的運行結果有力地證明了這一點:

  1. #include <stdio.h>
  2. int main(){
  3. int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
  4. int (*p)[4] = a;
  5. printf("%d\n", sizeof(*(p+1)));
  6. return 0;
  7. }

運行結果爲下面所示:

16

 

3) *(p+1)+1表示第 1 行第 1 個元素的地址。如何理解呢?

*(p+1)單獨使用時表示的是第 1 行數據,放在表達式中會被轉換爲第 1 行數據的首地址,也就是第 1 行第 0 個元素的地址,因爲使用整行數據沒有實際的含義,編譯器遇到這種情況都會轉換爲指向該行第 0 個元素的指針;就像一維數組的名字,在定義時或者和 sizeof、& 一起使用時才表示整個數組,出現在表達式中就會被轉換爲指向數組第 0 個元素的指針

 

4) *(*(p+1)+1)表示第 1 行第 1 個元素的值。很明顯,增加一個 * 表示取地址上的數據。

根據上面的結論,可以很容易推出以下的等價關係:

a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)

 【實例】使用指針遍歷二維數組。

  1. #include <stdio.h>
  2. int main(){
  3. int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
  4. int(*p)[4];
  5. int i,j;
  6. p=a;
  7. for(i=0; i<3; i++){
  8. for(j=0; j<4; j++) printf("%2d ",*(*(p+i)+j));
  9. printf("\n");
  10. }
  11. return 0;
  12. }

運行結果:

 0   1   2   3
 4   5   6   7
 8   9  10   11

指針數組和二維數組指針的區別

指針數組和二維數組指針在定義時非常相似,只是括號的位置不同:

  1. int *(p1[5]); //指針數組,可以去掉括號直接寫作 int *p1[5];
  2. int (*p2)[5];//二維數組指針,不能去掉括號

指針數組和二維數組指針有着本質上的區別:指針數組是一個數組,只是每個元素保存的都是指針,以上面的 p1 爲例,在32位環境下它佔用 4×5 = 20 個字節的內存。二維數組指針是一個指針,它指向一個二維數組,以上面的 p2 爲例,它佔用 4 個字節的內存。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章