指針是一個值為地址的變量,即存儲地址的變量,地址沒有數據類型之說,char *,int *都是一樣的長度,跟機器有關。
int *a表示a地址處存儲的值為整型。
指針的初始化
int a = 10; int *b = &a;
或者
1 int a = 10; 2 int *b; 3 b = &a;
指針的解引用
通過*b,可以對指針b進行解引用(間接訪問)從而訪問得到a的值。在指針解引用之前需要對它進行檢查,判斷b是否為NULL
指針常量和常量指針
int *p表示指向整型的指針,也叫整型指針,int const *p和int * const p兩個變量則不同,根據const和*的順序,int const *p中表示常量指針,int * const p表示指針常量,其中常量指針也可寫作const int *p。
常量指針(與整型指針類比),表示指針指向的值是常量,不可修改。指針常量,表示指針是常量,地址不可修改。
1 int a,b; 2 3 int * const p = &a; //指針常量 4 *p = 9; //操作成功 5 p = &b; //操作錯誤 6 7 int const * m = &a; //常量指針 8 *m = 9; //操作錯誤 9 m = &b; //操作成功
指向常量的指針常量該怎么寫?
1 const int * const b = &a;//指向常量的指針常量
數組
在討論一維和二維數組之前,先討論一下數組首地址和首元素地址。雖然首元素地址和首地址在數值上是相同的,但是它們所表示的意義卻不相同:
(1)數組的首元素地址:表示數組的首個元素的地址。
(2)數組的首地址:表示整個數組的地址。
只有使用“&數組名”時,才是取數組首地址;數組名或者&數組名[0]都是取得數組首元素地址,另外,首地址+1得到的是跳過整個數組的地址,首元素地址+1得到的是下一個元素的地址。
1.一維數組
int b[10],其中數組名b的值表示一個指針常量,是第一個元素的地址,該地址不可修改,但所指向的值可以修改。
當數組名在表達式中使用時,編譯器為它產生一個指針常量,除了兩種情況數組名不用指針常量來表示:sizeof和&。sizeof返回整個數組的長度(以字節為單位),而不是指向數組的指針的長度。&取一個數組名的地址所產生的是一個指向數組的指針,而不是一個指向指針常量的指針。
1 int a[10]; 2 int *c; 3 4 c = &a[0]; //第一個元素的地址 5 c = a; //c指向數組第一個元素 6 7 a = c; //錯誤,數組名是常量指針,該值不可修改 8 *a = 1;//指向的值可以修改
(1)一維數組下標訪問和指針間接訪問
聲明一個數組a,a和&a[0]表示同一個地址,對數組中的值進行訪問時,*a和a[0]都表示第一個元素,*(a+n)和a[n]同。
1 int a[10] = {0,1,2,3,4,5,6,7,8,9}; 2 3 int *b = a+1; //b指向a[1] 4 //int *b = &a[1]; 5 6 //從b的角度訪問 7 printf("%d", *b++); 8 printf("%d", b[0]); 9 printf("%d", b[-1]); 10 11 12 //從a的角度訪問 13 printf("%d", a[n]); 14 printf("%d", *(a+n));
上述代碼中3、4兩條語句表示的意思相同,表示b指向a[1]。C的下標引用和間接訪問表達式是一樣的,其中b[0]表示a[1],b[-1]表示a[0].
另外注意數組的間接訪問形式,a數組名是指針常量不可以用如下方式訪問:
1 printf("%d", *a++); 2 3 a+=n; 4 printf("%d", *a);
(2)一維數組進行函數傳參
一般的函數傳參分為傳值和傳址,傳值意味着對實參沒有影響,僅僅是對拷貝進行操作;傳址意味着可以訪問實參指針所指向的值,也可以對其進行操作。
數組傳參比較有趣的地方在於既是傳址也是傳值。傳址是傳遞數組的地址或首元素的地址,可以通過該指針對數組進行間接訪問,也修改數組值;傳值是因為,對形參指針的操作並不影響實參指針。
1 #include <stdio.h> 2 #include<stdlib.h> 3 4 void mytest_(int *a){ 5 a++; 6 printf("指針形參sizeof(a) = %d\n", sizeof(a)); 7 } 8 9 void mytest(int a[]){ 10 *a = 100; 11 printf("數組形參sizeof(a) = %d\n", sizeof(a)); 12 } 13 14 int main(){ 15 int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 16 printf("實參*a = %d\n", *a); 17 printf("數組sizeof(a) = %d\n", sizeof(a)); 18 19 mytest_(a); 20 printf("實參*a = %d\n", *a); 21 22 mytest(a); 23 printf("實參*a = %d\n", *a); 24 system("pause"); 25 return 0; 26 }
輸出如下結果:
另外在前面C語言操作符中提到過sizeof的操作數為數組名時,返回整個數組的長度,這個僅限於非傳參函數的數組,如代碼中main函數第一次sizeof所示。若數組作為參數進行傳參,即使形參是數組形式,在函數中依然表示為指針,因此更加准確的參數形式應該為指針而非數組名,此時sizeof表示的是指向整型的指針的長度,而不是數組的長度。
(3)字符數組和字符串常量初始化
1 char a[] = "HELLO"; 2 char *b = "HELLO";
字符數組初始化,可以寫明元素個數,也可不寫(自動計算數組的長度),但進行字符串處理時必須寫明元素個數。對字符數組進行初始化,看上去與字符串常量相同,a為字符數組,字符串常量b表示指向字符的指針。
2.二維數組
如果數組的維數不止一個,則為多維數組,這里以二維數組為例。
1 int a[6][10];
a表示二維數組,實質上可以說是一維數組的一維數組,包含了6個元素的數組,每個元素是包含了10個元素的數組。
a這個名字的值是指向它第一個元素的指針,所以a是一個指向10個整型元素的數組的指針,又稱數組指針。
1 char a[2][2] = {1, 2, 3, 4}; 2 3 4 //第二行第一個元素的地址 5 printf("%d\n", *(a + 1)); 6 printf("%d\n", a[1]); 7 printf("%d\n", &a[1][0]); 8 9 //第二行第一個元素 10 printf("%d\n", **(a + 1)); 11 printf("%d\n", *a[1]); 12 printf("%d\n", a[1][0]); 13 14 //第二行第二個元素地址 15 printf("%d\n", *(a + 1)+1); 16 printf("%d\n", a[1]+1); 17 printf("%d\n", &a[1][1]); 18 19 //第二行第二個元素 20 printf("%d\n", *(*(a + 1)+1)); 21 printf("%d\n", *(a[1]+1)); 22 printf("%d\n", a[1][1]);
(1)訪問地址
其中a是第一行元素的地址,*a和a[0]相同,表示第一行第一個元素的地址,&a[0][0]表示第一行第一個元素的地址,因此a=&a[0]=&(&a[0][0]),a表示地址的地址
a+n表示第n+1行元素地址,*(a+n)和a[n]相同,表示第n+1行第一個元素的地址,&a[n][0]表示第n+1行第一個元素的地址,因此a+n=&a[n]=&(&a[n][0])。
*(a+n)+n和a[n]+n表示第n+1行第n+1個元素地址
(2)訪問元素
從上面地址中可以看出,訪問第n+1行第n+1個元素,可以用a[n][n]和*(*(a+n)+n)
(3)二維數組進行函數傳參
二維數組int a[2][3]中a是一個指向整型數組的指針,又名數組指針。
int a[2][2]; int(*p)[2] = a;
多維數組進行函數傳參時,下面兩種形式任選一種即可
void func(int (*p)[2]); void func(int p[][2]);
二維數組傳參時編譯器必須知道第2個維度的長度才能對各下標進行求值(下標引用和間接訪問一樣),第1個維度的長度並不需要,因此可以寫成指針的形式。但不能將其聲明為
1 void func(int **p);
指向整型指針的指針和指向整型數組的指針不是一回事。首先整型數組並不是整型指針,只有在某些表達式中,可以作為數組名可以作為一個指針常量,而且二維數組作為函數傳參只能省略第一維參數 。