對於二維指針,稱為指向指針的指針變量,**p也就是*(*p)。下面是一個例子
int a[5] = { 1, 3, 5, 7, 9 }; int *num[5], i; int **p; for (i = 0; i < 5; i++) { num[i] = &a[i]; printf("&a[%d]=%d,num[%d]=%d,&num[%d]=%d\n", i, &a[i], i, num[i], i,&num[i]); } p = num + 2; printf("p=%d,*p=%d,**p=%d", p, *p, **p);
結果是
&a[0]=2686756,num[0]=2686756,&num[0]=2686736 &a[1]=2686760,num[1]=2686760,&num[1]=2686740 &a[2]=2686764,num[2]=2686764,&num[2]=2686744 &a[3]=2686768,num[3]=2686768,&num[3]=2686748 &a[4]=2686772,num[4]=2686772,&num[4]=2686752 p=2686744,*p=2686764,**p=5
其中,指針數組num存放的是int型數組中個元素的地址,所以num[i]==&a[i]。而且num[i]是指針數組,也有自己的一個地址。若
令p=num+2,其中p是指向指針的指針變量,則p中存放的就是num中元素的地址,也就是num[i]的地址。而*p就是num[i]中存放的數據,這個數據就是a[i]的地址。
所以**p就是a[i]的值。
下面這篇文章是轉自cdsn上的一篇。
在C語言中, 允許用一個變量來存放指針,這種變量稱為指針變量。因此, 一個指針變量的值就是某個內存單元的地址或稱為某內存單元的指針。
為了避免混淆,我們約定:“指針”是指地址, 是常量,“指針變量”是指取值為地址的變量。 定義指針的目的是為了通過指針去訪問內存單元。
凡是出現數組,函數的地方都可以用一個指針變量來表示, 只要該指針變量中賦予數組或函數的首地址即可。
- 指針變量的類型說明
對指針變量的類型說明包括三個內容:
- 指針類型說明,即定義變量為一個指針變量;
- 指針變量名;
- 變量值(指針)所指向的變量的數據類型。
int *p1;表示p1是一個指針變量,它的值是某個整型變量的地址。
staic int *p2; /*p2是指向靜態整型變量的指針變量*/
float *p3; /*p3是指向浮點變量的指針變量*/
char *p4; /*p4是指向字符變量的指針變量*/
應該注意的是,一個指針變量只能指向同類型的變量,如P3 只能指向浮點變量,不能時而指向一個浮點變量, 時而又指向一個字符變量。
2.指針變量的賦值
(1)指針變量初始化的方法 int a;
int *p=&a;
(2)賦值語句的方法 int a;
int *p;
p=&a;
不允許把一個數賦予指針變量,故下面的賦值是錯誤的: int *p;p=1000; 被賦值的指針變量前不能再加“*”說明符,如寫為*p=&a 也是錯誤的
3.指針變量的運算
指針變量只能進行賦值運算和部分算術運算及關系運算。
(1)取地址運算符&
(2)取內容運算符*
用來表示指針變量所指的變量,在*運算符之后跟的變量必須是指針變量。需要注意的是指針運算符*和指針變量說明中的指針說明符* 不是一回事。
(3)指針變量的運算
(1)賦值運算
指針變量的賦值運算有以下幾種形式:
①指針變量初始化賦值,前面已作介紹。
②把一個變量的地址賦予指向相同數據類型的指針變量。例如:
int a,*pa; pa=&a; /*把整型變量a的地址賦予整型指針變量pa*/
③把一個指針變量的值賦予指向相同類型變量的另一個指針變量。如:
int a,*pa=&a,*pb; pb=pa; /*把a的地址賦予指針變量pb*/
/*由於pa,pb均為指向整型變量的指針變量,因此可以相互賦值。
④把數組的首地址賦予指向數組的指針變量。
int a[5],*pa; pa=a; /*數組名表示數組的首地址,故可賦予指向數組的指針變量pa*/
//也可寫為:
pa=&a[0]; /*數組第一個元素的地址也是整個數組的首地址, 也可賦予pa*/
//當然也可采取初始化賦值的方法:
int a[5],*pa=a;
⑤把字符串的首地址賦予指向字符類型的指針變量。
char *pc;
pc="c language"; //或用初始化賦值的方法寫為: char *pc="C Language";
這里應說明的是並不是把整個字符串裝入指針變量, 而是把存放該字符串的字符數組的首地址裝入指針變量。 在后面還將詳細介紹。
⑥把函數的入口地址賦予指向函數的指針變量。
int (*pf)(); pf=f; /*f為函數名*/
(2)加減算術運算
對於指向數組的指針變量,可以加上或減去一個整數n。設pa是指向數組a的指針變量,則pa+n,pa-n,pa++,++pa,pa--,--pa 運算都是合法
的。指針變量加或減一個整數n的意義是把指針指向的當前位置(指向某數組元素)向前或向后移動n個位置。
應該注意,數組指針變量向前或向后移動一個位置和地址加1或減1 在概念上是不同的。因為數組可以有不同的類型, 各種類型的數組元素
所占的字節長度是不同的。如指針變量加1,即向后移動1 個位置表示指針變量指向下一個數據元素的首地址。而不是在原地址基礎上加1。
nt a[5],*pa; pa=a; /*pa指向數組a,也是指向a[0]*/ pa=pa+2; /*pa指向a[2],即pa的值為&pa[2]*/
指針變量的加減運算只能對數組指針變量進行, 對指向其它類型變量的指針變量作加減運算是毫無意義的。
(3)兩個指針變量之間的運算只有指向同一數組的兩個指針變量之間才能進行運算, 否則運算毫無意義。
①兩指針變量相減
兩指針變量相減所得之差是兩個指針所指數組元素之間相差的元素個數。實際上是兩個指針值(地址) 相減之差再除以該數組元素的長度
(字節數)。例如pf1和pf2 是指向同一浮點數組的兩個指針變量,設pf1的值為2010H,pf2的值為2000H,而浮點數組每個元素占4個字節,
所以pf1-pf2的結果為(2000H-2010H)/4=4,表示pf1和 pf2之間相差4個元素。兩個指針變量不能進行加法運算。 例如,pf1+pf2是什么意
思呢?毫無實際意義。
②兩指針變量進行關系運算
指向同一數組的兩指針變量進行關系運算可表示它們所指數組元素之間的關系。
pf1==pf2表示pf1和pf2指向同一數組元素
pf1>pf2表示pf1處於高地址位置
pf1<pf2表示pf2處於低地址位置
注:指針變量還可以與0比較。設p為指針變量,則p==0表明p是空指針,它不指向任何變量;p!=0表示p不是空指針。指針變量未賦值時,可以
是任意值,是不能使用的。否則將造成意外錯誤。而指針變量賦0值后,則可以使用,只是它不指向具體的變量而已。
4.數組指針變量的說明和使用
int a[5],*pa; pa=a;
pa,a,&a[0]均指向同一單元,它們是數組a的首地址,也是0 號元素a[0]的首地址。pa+1,a+1,&a[1]均指向1號元素a[1]。類推可知a+i,a+i,&a[i]指向i號
元素a[i]。應該說明的是pa是變量,而a,&a[i]都是常量。引入指針變量后,就可以用兩種方法來訪問數組元素了。
第一種方法為下標法,即用a[i]形式訪問數組元素。
第二種方法為指針法,即采用*(pa+i)形式,用間接訪問的方法來訪問數組元素。
int a[5], i, *pa; pa = a;
//將變量i的值賦給由指針pa指向的a[]的數組單元 for (i = 0; i < 5; i++) { *pa = i; pa++; } pa = a; for (i = 0; i < 5; i++) { printf("a[%d]=%d\n", i, *pa); pa++; }
5.數組名和數組指針變量作函數參數
float aver(float *pa); main() { float sco[5], av, *sp; int i; sp = sco; printf("\ninput 5 scores:\n"); fflush(stdout); for (i = 0; i < 5; i++) scanf("%f", &sco[i]); av = aver(sp); printf("average score is %5.2f", av); } float aver(float *pa) { int i; float av, s = 0; for (i = 0; i < 5; i++) s = s + *pa++; av = s / 5; return av; }
6.指向多維數組的指針變量
C語言允許把一個二維數組分解為多個一維數組來處理。因此數組a[3][4]可分解為三個一維數組,即a[0],a[1],a[2]。每一個一維數組又含有四個元素。
例如a[0]數組,含有a[0][0],a[0][1],a[0][2],a[0][3]四個元素。數組及數組元素的地址表示如下:a是二維數組名,也是二維數組0行的首地址,等於
1000。a[0]是第一個一維數組的數組名和首地址,因此也為1000。*(a+0)或*a是與a[0]等效的, 它表示一維數組a[0]0 號元素的首地址。 也為1000。
&a[0][0]是二維數組a的0行0列元素首地址,同樣是1000。因此,a,a[0],*(a+0),*a,&a[0][0]是相等的。同理,a+1是二維數組1行的首地址,等於
1008。a[1]是第二個一維數組的數組名和首地址,因此也為1008。&a[1][0]是二維數組a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等
同的。 由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。 此外,&a[i]和a[i]也是等同的。
因為在二維數組中不能把&a[i]理解為元素a[i]的地址,不存在元素a[i]。
a[i]+j是一維數組a[i]的j號元素首地址,它等於&a[i][j]。由a[i]=*(a+i)得a[i]+j=*(a+i)+j,由於*(a+i)+j是二維數組a的i行j列元素的首地址。
該元素的值等於*(*(a+i)+j)。
#define PF "%d,%d,%d,%d,%d,/n" main() { static int a[3][4]= {0,1,2,3,4,5,6,7,8,9,10,11}; printf(PF,a,*a,a[0],&a[0],&a[0][0]); printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]); printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]); printf("%d,%d\n",a[1]+1,*(a+1)+1); printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1)); }
-
- 把二維數組a 分解為一維數組a[0],a[1],a[2]之后,設p為指向二維數組的指針變量。可定義為: int (*p)[4] 它表示p是一個指針變量,它指向二維數組a 或指向第一個一維數組a[0],其值等於a,a[0],或&a[0][0]等。
而p+i則指向一維數組a[i]。從前面的分析可得出*(p+i)+j是二維數組i行j 列的元素的地址,而*(*(p+i)+j)則是i行j列元素的值。
2. 二維數組指針變量說明的一般形式為: 類型說明符 (*指針變量名)[長度] 其中“類型說明符”為所指數組的數據類型。“*”表示其后的變量是指針類
型。 “長度”表示二維數組分解為多個一維數組時, 一維數組的長度,也就是二維數組的列數。
main() { static 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)); }
main() { char *ps = "this is a book"; int n = 10; ps = ps + n; printf("%s\n", ps); } /*運行結果為:book
在程序中對ps初始化時,即把字符串首地址賦予ps,當ps= ps+10之后,ps指向字符“b”,因此輸出為"book"。*/
7.使用字符串指針變量與字符數組的區別
1.字符串指針變量本身是一個變量,用於存放字符串的首地址。而字符串本身是存放在以該首地址為首的一塊連續的內存空間中並以‘/0’作為串的結
束。字符數組是由於若干個數組元素組成的,它可用來存放整個字符串。
2. 對字符數組作初始化賦值,必須采用外部類型或靜態類型,如: static char st[]={“C Language”};
而對字符串指針變量則無此限制,如: char *ps="C Language";
3. 對字符串指針方式 char *ps="C Language";可以寫為: char *ps; ps="C Language";而對數組方式:
static char st[]={"C Language"};
不能寫為:
char st[20];st={"C Language"};而只能對字符數組的各元素逐個賦值。
8.函數指針變量
函數指針變量定義的一般形式為:類型說明符 (*指針變量名)();例如: int (*pf)();
int max(int a, int b) { if (a > b) return a; else
return b; } main() { int max(int a, int b); int (*pmax)(); int x, y, z; pmax = max; printf("input two numbers:\n"); fflush(stdout); scanf("%d%d", &x, &y); z = (*pmax)(x, y); printf("maxmum=%d", z); }
從上述程序可以看出用,函數指針變量形式調用函數的步驟如下:
-
-
- 先定義函數指針變量,如上程序中第9行 int (*pmax)();定義pmax為函數指針變量。
- 把被調函數的入口地址(函數名)賦予該函數指針變量,如程序中第11行 pmax=max;
- 用函數指針變量形式調用函數,如程序第15行 z=(*pmax)(x,y); 調用函數的一般形式為: (*指針變量名) (實參表)
-
使用函數指針變量還應注意以下兩點:
-
- 函數指針變量不能進行算術運算,這是與數組指針變量不同的。數組指針變量加減一個整數可使指針移動指向后面或前面的數組元素,而函數指針的移動是毫無意義的。
- 函數調用中"(*指針變量名)"的兩邊的括號不可少,其中的*不應該理解為求值運算,在此處它只是一種表示符號
9.指針型函數
函數名之前加了“*”號表明這是一個指針型函數,即返回值是一個指針。
注:int(*p)()和int *p()是兩個完全不同的量。int(*p)()是一個變量說明,說明p 是一個指向函數入口的指針變量,該函數的返回值是整型量。
int *p() 則不是變量說明而是函數說明,說明p是一個指針型函數,其返回值是一個指向整型量的指針,*p兩邊沒有括號
main() { int i; char *day_name(int n); printf("input Day No:\n"); scanf("%d", &i); if (i < 0) exit(1); printf("Day No:%2d-->%s\n", i, day_name(i)); } char *day_name(int n) { static char *name[] = { "Illegal day", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; return ((n < 1 || n > 7) ? name[0] : name[n]); }
本程序是通過指針函數,輸入一個1~7之間的整數, 輸出對應的星期名。
指針數組是一組有序的指針的集合。 指針數組的所有元素都必須是具有相同存儲類型和指向相同數據類型的指針變量。
10.指針數組
指針數組說明的一般形式為: 類型說明符 *數組名[數組長度]
int *pa[3] 表示pa是一個指針數組,它有三個數組元素, 每個元素值都是一個指針,指向整型變量。
11.小結
1. 指針是C語言中一個重要的組成部分,使用指針編程有以下優點:
(1)提高程序的編譯效率和執行速度。
(2)通過指針可使用主調函數和被調函數之間共享變量或數據結構,便於實現雙向數據通訊。
(3)可以實現動態的存儲分配。
(4)便於表示各種數據結構,編寫高質量的程序。
2. 指針的運算
(1)取地址運算符&:求變量的地址
(2)取內容運算符*:表示指針所指的變量
(3)賦值運算
·把變量地址賦予指針變量
·同類型指針變量相互賦值
·把數組,字符串的首地址賦予指針變量
·把函數入口地址賦予指針變量
(4)加減運算
對指向數組,字符串的指針變量可以進行加減運算,如p+n,p-n,p++,p--等。對指向同一數組的兩個指針變量可以相減。對指向其它類型的指針變
量作加減運算是無意義的。
(5)關系運算
指向同一數組的兩個指針變量之間可以進行大於、小於、 等於比較運算。指針可與0比較,p==0表示p為空指針。
3. 與指針有關的各種說明和意義見下表。
int *p; p為指向整型量的指針變量
int *p[n]; p為指針數組,由n個指向整型量的指針元素組成。
int (*p)[n]; p為指向整型二維數組的指針變量,二維數組的列數為n
int *p() p為返回指針值的函數,該指針指向整型量
int (*p)() p為指向函數的指針,該函數返回整型量
int **p p為一個指向另一指針的指針變量,該指針指向一個整型量。
4. 有關指針的說明很多是由指針,數組,函數說明組合而成的。
但並不是可以任意組合,例如數組不能由函數組成,即數組元素不能是一個函數;函數也不能返回一個數組或返回另一個函數。例如
int a[5]();就是錯誤的。
5. 關於括號
在解釋組合說明符時, 標識符右邊的方括號和圓括號優先於標識符左邊的“*”號,而方括號和圓括號以相同的優先級從左到右結合。但可以用圓括號
改變約定的結合順序。
6. 閱讀組合說明符的規則是“從里向外”。
從標識符開始,先看它右邊有無方括號或園括號,如有則先作出解釋,再看左邊有無*號。 如果在任何時候遇到了閉括號,則在繼續之前必須用
相同的規則處理括號內的內容。
例如:
int*(*(*a)())[10]
↑ ↑↑↑↑↑↑
7 6 4 2 1 3 5
上面給出了由內向外的閱讀順序,下面來解釋它:
(1)標識符a被說明為;
(2)一個指針變量,它指向;
(3)一個函數,它返回;
(4)一個指針,該指針指向;
(5)一個有10個元素的數組,其類型為;
(6)指針型,它指向;
(7)int型數據。
因此a是一個函數指針變量,該函數返回的一個指針值又指向一個指針數組,該指針數組的元素指向整型量。