關於指針數組與數組指針詳解(知識點全面)_利刃Cc的博客-CSDN博客_指針數組的作用
1.指針數組
如果一個數組中的所有元素保存的都是指針,那麼我們就稱它爲指針數組。其一般形式爲:
數據類型 *數組名[常量表達式][常量表達式]...... ;
它是一個數組,數組的元素都是指針,數組佔多少個字節由數組本身的大小決定,每個元素都是一個指針。
例如:char *arr[]={“Sunday”,“Monday”},存儲了兩個指針,第一個指針指向了字符串"Sunday",第二個指針指向了字符串"Monday",而sizeof(arr)=8,因爲在32位平臺,指針類型大小佔4個字節。指針數組最重要的用途是對多個字符串進行處理操作,因爲字符指針比二維數組更快更有效。
下面是個簡單的例子
-
-
int main()
-
{
-
//定義三個整型數組
-
int a[5] = { 1,2,3,4,5 };
-
int b[5] = { 6,4,8,3,1 };
-
int c[5] = { 2,5,8,6,1 };
-
//定義一個存放指向整型變量的指針的數組arr
-
int* arr[] = { a,b,c };
-
//通過接引用打印出三個一維數組的元素
-
for (int i = 0; i < 3; i++)
-
{
-
for (int j = 0; j < 5; j++)
-
{
-
printf("%d ", *(arr[i]+j));
-
}
-
printf("\n");
-
}
-
return 0;
-
}
結果如下:
1 2 3 4 5
6 4 8 3 1
2 5 8 6 1
以上對arr解引用的方式有很多,它們都是等價的,我們來舉個例子:
-
-
int main()
-
{
-
int i = 0;
-
int a[3][4] = { {1,2,3,4} ,{5,6,7,8} ,{9,10,11,12} };//定義一個二維數組
-
int* pa[3];//定義一個指針數組
-
for (i = 0; i < 3; i++)//給指針數組賦值
-
pa[i] = a[i];
-
printf("指針數組的內容爲:\n");
-
for (i = 0; i < 3; i++)//打印出指針數組的內容
-
{
-
int j;
-
for (j = 0; j < 4; j++)
-
printf("%d ", *(*(pa + i) + j));
-
printf("\n");
-
}
-
//以下均爲不同方式的解引用操作
-
printf("不同解引用操作的結果爲:\n");
-
printf("%d,%d\n", a[1][1], *(pa[1] + 1));
-
printf("%d,%d\n", a[1][1], *(*(pa+1) + 1));
-
printf("%d,%d\n", a[1][1], (*(pa + 1))[1]);
-
printf("%d,%d\n", a[1][1], pa[1][1]);
-
return 0;
-
}
結果如下所示:
指針數組的內容爲:
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):指針數組還可以和字符串數組相結合使用,請看下面的例子:
-
-
int main(){
-
char *str[3] = {"lirendada","C語言","C Language"};
-
-
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
-
return 0;
-
}
結果如下:
lirendada
c語言
C Language
需要注意的是,字符數組 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位於其他的內存區域,和字符數組是分開的。
也只有當指針數組中每個元素的類型都是char *
時,才能像上面那樣給指針數組賦值,其他類型不行。
爲了便於理解,可以將上面的字符串數組改成下面的形式,它們都是等價的。
-
-
int main(){
-
char *str0 = "lirendada";
-
char *str1 = "C語言";
-
char *str2 = "C Language";
-
char *str[3] = {str0, str1, str2};
-
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
-
return 0;
-
}
補充(2):二維數組與指針數組的區別
-
char *p1[]={"lirendada","C","C++"};
-
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 個元素,下面的運行結果有力地證明了這一點:
-
-
int main(){
-
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
-
int (*p)[4] = a;
-
printf("%d\n", sizeof(*(p+1)));
-
return 0;
-
}
運行結果爲下面所示:
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)
【實例】使用指針遍歷二維數組。
-
-
int main(){
-
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
-
int(*p)[4];
-
int i,j;
-
p=a;
-
for(i=0; i<3; i++){
-
for(j=0; j<4; j++) printf("%2d ",*(*(p+i)+j));
-
printf("\n");
-
}
-
return 0;
-
}
運行結果:
0 1 2 3 4 5 6 7 8 9 10 11
指針數組和二維數組指針的區別
指針數組和二維數組指針在定義時非常相似,只是括號的位置不同:
- int *(p1[5]); //指針數組,可以去掉括號直接寫作 int *p1[5];
- int (*p2)[5];//二維數組指針,不能去掉括號
指針數組和二維數組指針有着本質上的區別:指針數組是一個數組,只是每個元素保存的都是指針,以上面的 p1 爲例,在32位環境下它佔用 4×5 = 20 個字節的內存。二維數組指針是一個指針,它指向一個二維數組,以上面的 p2 爲例,它佔用 4 個字節的內存。