1、基礎概念
在 c 語言裏,基本變量存放的是數據,而指針變量是存放地址的變量(另一變量額地址)。在 c 語言中,指針也稱之爲地址,所以我們常說某某變量的指針,可以理解爲某某變量的在內存中的地址。如:b 變量的指針是 1000,可以理解爲 b 在內存中的地址是 1000,注意不能理解爲 b 的指針變量是4004(錯誤的理解)。
2、定義指針變量
在 c 語言指針的定義形式如下:
類型說明符 *指針變量名
如:
int *p1,*p2; //錯誤的定義方式 int* p1,p2;// p1 整形指針,p2 是整形變量
在 c 裏,可以在變量前加 & 符號取得變量的地址,同樣地,可以在指針變量前加 * 符號取得指針變量指向的變量的值(該內存地址上存放的數據),如:
3、指針變量作爲函數參數int a = 4; int *p = &a; printf("指針 p 指向的變量的值是 %d \r\n",*p);// 注意在這行代碼裏, p 是指針, *p 是指向的變量的值
//帶有指針變量的函數定義形式如下 void fun(int *p1,int *p2) //調用方式如下: int *ptr1 = &a1; int *ptr2 = &a2; fun(ptr1,ptr2)
注:在被調用函數中,試圖修改指針變量的值(即地址)來達到改變實參的目的都是錯誤的.
這句話如何理解呢?這裏牽涉到一個比較容易混淆的問題-值傳遞和引用傳遞的區別.在這裏提供一種理解方式,如下:將實參傳給形參的時候,形參都是將實參的內容複製一遍,再調用相對應的函數.
void swap1(int num1,int num2){ int temp = num1; num1 = num2; num2 = temp; } void swap2(int *num1,int *num2){ // 試圖修改指針變量的值(地址) int *temp = num1; num1 = num2; num2 = temp; } void swap3(int *num1,int *num2){ int temp = *num1; *num1 = *num2; *num2 = temp; } int main(){ int a = 1,b = 2; int *ptr1 = &a,*ptr2 = &b; swap1(a,b); // 結果不變:a = 1,b = 2 swap2(ptr1,ptr2); // 結果不變,a = 1,b = 2 swap3(ptr1,ptr2); // a = 2, b =1 }
4、字符串與指針
字符串是字符的集合,可以使用數組(指針常量)或指針變量來表示。
char *str1 = "tianya"; char str2[] = "tianya";
在上面代碼中: str1 是一個指針變量,str2 是指針常量,所以 str2 不能使用 ++ 或 —— 運算符。輸入對應字符串的值如下:
printf("str1 的值爲 %s \n",str1); printf("str2 的值爲 %s \n",str2);
另外,我們還需要理解一下,下面這幾個的定義方式的不同:
char *str = "tianya"; char str[] = "tianya"; char str[10] = "tianya"; char str[2] = "tianya";
int a[5]; // a 其實是一個常量指針,指向數組的第一個元素 a[0] int *p = a; // 等效於:int *p = &a[0];
2) 通過指針引用數組元素
int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("the seconde elem is %d \n",a[1]);//20 printf("the seconde elem is %d \n",*(a+1));//20 printf("the seconde elem is %d \n",p[1]);//20 printf("the seconde elem is %d \n",*(p+1));//20
類似地推算,我們可以知道:a[i] 等同於 *(a+i),當中 a[0] 等同於 *a 或 *(a+0);*(p+i) 等同於 ptr[i]。看過一些書,會有着這樣一種介紹 * 和 [] 等同,,結合前面的例子,我們再看另外一個例子:
int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("the seconde elem is %d \n",a[1]+1);//21 printf("the seconde elem is %d \n",*a+1);//11,可以看成 (*a)+1 或 *(a+0)+1 printf("the seconde elem is %d \n",p[1]+1);//21 printf("the seconde elem is %d \n",*p+1);//11,可以看成 (*p)+1 或 *(p+0)+1
是不是覺得我們基本可以瞭解這些使用了,我們再深入地學習一下,當 * 與 ++ 或 —— 相遇時,又會是一種什麼樣的情況:
// 第一種情況 int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("*p++ = %d \n",*p++); // 10,可以看成是 *(p++) // 第二種情況: int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("*++p = %d \n",*++p); //20,可以看成 *(++p) // 第三種情況 int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("++*p = %d \n",++*p); // 11,可以看成 ++(*p)
* 與 ++ 或 -- 的運算符優先級相同,結合性是由右往左。如果覺得基本都懂的,那猜猜一下下面這個例子會輸出什麼?
int a[5] = {10,20,30,40,50}; int *p = a; // 或 int *p = &a[0]; printf("*p++ = %d \n",*p++); printf("*++p = %d \n",*++p); printf("++*p = %d \n",++*p);
現在的我們可以來總結一下:
當 p = &a[i] 時,則有:
- *p++ 相當於 a[i++];
- *++p 相當於 a[++i];
- *p-- 相當於 a[i--];
- *—p 相當於 a[—i];
6、二維數組和指針
在前面,我們已經學過,一維數組的元素值,可以使用 [] 或 * 來獲取;類似地,在二維數組裏,我們可以使用 ** 、[][] 或 一個 * 與一個 [] 獲得,其餘的表示法(即一個 * 或 一個 [])只能得到地址。介紹如下:
// 取第二行第一列的地址和值 int a[2][3] = {0,1,2,3,4,5}; printf("address:%p, value:%d \n",&a[1][0],a[1][0]); printf("address:%p, value:%d \n",a[1]+0,*(a[1]+0)); printf("address:%p, value:%d \n",*(a+1)+1,*(*(a+1)+0));
在二維數組a裏,可以這樣取得第 i 行,第 j 列的地址:
&a[i][j] , a[i]+j,*(a+i)+j
對應的,我們只需要在地址前加一個 * 即可得到其對應的值:
a[i][j],*(a[i]+j),*(*(a+i)+j)
那現在我們思考一下,應該使用什麼樣的指針指向二維數組呢??
指向數組的指針 (*p)[] 指向二維數組了。介紹如下:
int a[2][3]={0,1,2,3,4,5}; int (*p)[3] = a; int i,j; for( i = 0; i < 2;i ++){ for(j = 0;j < 3;j++){ printf("a[%d][%d] = %d ",i,j,*(*(p+i)+j)); } printf("\n"); }