首先確定優先級:()>[]>*,另外數組指針,類比整型指針,表示指向數組的指針;指針數組,類比整型數組,表示元素為指針的數組。
數組指針
int (*p)[n] 首先()優先級高,它是一個指針,指向一個整型數組。n為數組的長度,當p+1時需要跨越n個整型數據的長度,通常用來表示二維數組及二維數組的函數傳參。
一維數組賦值給數組指針時,需要取數組地址或對其進行強制轉換,另外相當於二維數組中只有一個行元素,p3+1的地址沒有意義,*(p3+1)也無法顯示。
1 char a[5] = { 'A', 'B', 'C', 'D' }; 2 char(*p3)[5] = &a; 3 char(*p4)[5] = (char (*)[5])a;
二維數組賦值給數組指針時,具體間接訪問格式,可以參考上一篇博文指針和數組的關系。
1 # include <stdio.h> 2 int main(void) 3 { 4 int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 5 int i, j; 6 int (*p)[4] = a; //記住這種定義格式 7 for (i=0; i<3; ++i) 8 { 9 for (j=0; j<4; ++j) 10 { 11 printf("%-2d\x20", *(*(p+i)+j)); /*%-2d中, '-'表示左對齊, 如果不寫'-'則默認表示右對齊;2表示這個元素輸出時占兩個空格的空間*/ 12 } 13 printf("\n"); 14 } 15 return 0; 16 }
二維數組元素地址賦值給一維指針,對元素進行訪問時需要跨越n個整型數據的長度,即p + i*N +j == &a[i][j]
1 # include <stdio.h> 2 int main(void) 3 { 4 int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; 5 int i, j; 6 int *p = &a[0][0]; //把a[0][0]的地址賦給指針變量p 7 for (i=0; i<3; ++i) 8 { 9 for (j=0; j<4; ++j) 10 { 11 printf("%-2d\x20", *(p+i*4+j)); 12 } 13 printf("\n"); 14 } 15 return 0; 16 }
指針數組
int *p[n] 首先[]優先級高,它是一個數組,前面為int*,表示數組的元素為整型指針,也可表示為int **p, 這里執行p+1時,則p指向下一個數組元素,這樣賦值是錯誤的:p=a;因為p是個不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它們分別是指針變量,可以用來存放變量地址。
其中C中處理字符串和鏈表數據結構中用該數據類型較多,涉及到二級指針時也容易出問題。
1 int a = 1; 2 int b = 2; 3 int *p[2]; 4 p[0] = &a; 5 p[1] = &b; 6 7 printf("%p\n", p[0]); //a的地址 8 printf("%p\n", &a); //a的地址 9 printf("%p\n", p[1]); //b的地址 10 printf("%p\n", &b); //b的地址 11 printf("%d\n", *p[0]); //p[0]表示a的地址,則*p[0]表示a的值 12 printf("%d\n", *p[1]); //p[1]表示b的地址,則*p[1]表示b的值 13 14 15 //將二維數組賦給指針數組 16 int *pp[3]; //一個一維數組內存放着三個指針變量,分別是p[0]、p[1]、p[2],所以要分別賦值 17 int c[3][4]; 18 for (int i = 0; i < 3; i++) 19 pp[i] = c[i];
數組指針是一個指針變量,占有內存中一個指針的存儲空間;
指針數組是多個指針變量,以數組的形式存儲在內存中,占有多個指針的存儲空間。
指向二維數組時,指針數組和數組指針訪問數組元素時完全相同,但函數傳參時,只能用數組指針,原因參見指針和數組的關系。
int a[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; //2行3列的二維整型數組 int(*p)[3]; //數組指針,指向含有3個元素的一維數組 int *q[2]; //指針數組,一個數組內存放2個指針變量 p = a; q[0] = a[0]; q[1] = a[1]; //輸出第1行第2列的值 printf("%d\n", a[1][2]); //5 printf("%d\n", *(p[1] + 2)); //5 printf("%d\n", *(*(p + 1) + 2)); //5 printf("%d\n", (*(p + 1))[2]); //5 printf("%d\n", p[1][2]); //5 printf("%d\n", *(q[1] + 2)); //5 printf("%d\n", *(*(q + 1) + 2)); //5 printf("%d\n", (*(q + 1))[2]); //5 printf("%d\n", q[1][2]); //5
一級指針和二級指針
在進行函數指針傳參時,編譯器總是要為函數的每個參數制作臨時副本(即形參為實參的副本),在調用的時候,實參和形參是不同的內存空間,只是,這個內存空間存放的指針指向的是同一塊地址 ,所以形參在函數執行中可以訪問實參指向的內存空間,但是形參的指向的改變(即形參所指向的內存空間與實參不同)並不能影響實參,但改變形參所指向的空間的值時(改變形參指向的內存空間的值),實參指向會改變。
一級指針的作用:在函數外部定義一個值m,在函數內對m進行賦值改變,函數結束后對值m生效。一級指針不管形參、實參都指向值m的地址,對一級指針進行解引用時都是訪問m的地址空間,然后訪問m,因此若改變形參指向的空間的值(即改變了m的值),則實參也會改變。
二級指針作用:在函數外部定義一個指針p,在函數內給指針賦值,函數結束后對指針p生效。二級指針不管形參、實參都指向指針p,對二級指針進行解引用時都是先訪問指針p的地址空間,再訪問指針p,因此若改變形參指向的空間的值(即改變了指針p的值),則實參也會改變。
如果用了二級指針,但不想改變外部的指針p,可以在函數內部對二級指針指向的空間的值做一個拷貝。
1 void my_malloc(char **s) 2 { 3 *s=(char*)malloc(100); 4 } 5 6 void main() 7 { 8 char *p=NULL; 9 my_malloc(&p); 10 //do something 11 if(p) 12 free(p); 13 }
這里給指針p分配內存,do something,然后free(p),如果用一級指針,那么就相當於給一個p的拷貝s分配內存,p依然沒分配內存,用二級指針之后,才對p分配了內存。
應用上面的二級指針的例子
1 void getstring_(char **p){ 2 //char *p; 3 *p = malloc(100); 4 memset(*p, 0, 100); 5 strcpy(*p, "hello"); 6 } 7 8 void test_(){ 9 char *ret = NULL; 10 getstring_(&ret); 11 printf("%s\n", ret); 12 } 13 14 int main(){ 15 test_(); 16 17 system("pause"); 18 return 0; 19 }