一個指針是一個地址,是一個常量。而一個指針變量卻可以被賦予不同的指針值,是變量。
我們中約定:“指針”是指地址,是常量,“指針變量”是指取值爲地址的變量。定義指針的目的是爲了通過指針去訪問內存單元。
指針變量的值是一個地址,那麼這個地址不僅可以是變量的地址,也可以是其它數據結構的地址。在一個指針變量中存放一個數組或一個函數的首地址有何意義呢? 因爲數組或函數都是連續存放的。通過訪問指針變量取得了數組或函數的首地址,也就找到了該數組或函數。這樣一來,凡是出現數組,函數的地方都可以用一個指針變量來表示,只要該指針變量中賦予數組或函數的首地址即可。這樣做,將會使程序的概念十分清楚,程序本身也精練,高效。在C語言中,一種數據類型或數據結構往往都佔有一組連續的內存單元。 用“地址”這個概念並不能很好地描述一種數據類型或數據結構,而“指針”雖然實際上也是一個地址,但它卻是一個數據結構的首地址,它是“指向”一個數據結構的,因而概念更爲清楚,表示更爲明確。
指針的加減法:
只針對指向數據的指針有意義,指向其他的類型變量無意義。這裏的加減表示,指針向前或向後移動一個位置,在數組上來說,就是上一個元素或下一個元素的區別,跟我們的地址值加一減一不一樣。
實例說明:
*P++,因爲*與++優先級相同,根據右結合,所以可以寫成*(P++),這樣更容易看懂。 指針變量加減時,是數組前移或後移。
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}}
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]。C語言規定,它是一種地址計算方法,表示數組a第i行首地址。由此,我們得出:a[i],&a[i],*(a+i)和a+i也都是等同的。
另外,a[0]也可以看成是a[0]+0,是一維數組a[0]的0號元素的首地址,而a[0]+1則是a[0]的1號元素首地址,由此可得出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)。
因爲是二維數組,所以*a指向的並不是二維數組中的一個數,而是一個地址,*(a+1)同樣是一個地址;只有*(*(a+1)+2)與*(a[1]+2)這個指向的纔是二維數組中的元素。二維指針的表達形式爲:
類型說明符 (*指針變量名)[長度]
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
把二維數組a分解爲一維數組a[0],a[1],a[2]之後,設p爲指向二維數組的指針變量。可定義爲:int (*p)[4]
注意:指針變量名與*必須要用括號包起來。
例子:
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");}
}
函數指針:
函數指針變量定義的一般形式爲:
類型說明符 (*指針變量名)();
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");
scanf("%d%d",&x,&y);
z=(*pmax)(x,y);
printf("maxmum=%d",z);
}
1) 調用函數的一般形式爲:
(*指針變量名) (實參表)
指針型函數
在C語言中允許一個函數的返回值是一個指針(即地址),這種返回指針值的函數稱爲指針型函數。
定義指針型函數的一般形式爲:
類型說明符 *函數名(形參表)
{
…… /*函數體*/
}
例子:
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]);
}
指針數組:
main(){
int a[3][4]={0,1,2,3,4,5,6,7,8,9};
int *pa[3]={a[0],a[1],a[2]};
int *p=a[0];
int i;
for(i=0;i<3;i++)
printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i));
printf("=======================\n");
for(i=0;i<3;i++)
printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));
}
main(){
static char *name[]={ "Illegal day",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"};
char *ps;
int i;
char *day_name(char *name[],int n);
printf("input Day No:\n");
scanf("%d",&i);
printf("input Day after %s:\n",*name);
ps=day_name(name,i);
printf("Day No:%2d-->%s\n",i,ps);
}
char *day_name(char *name[],int n)
{
char *pp1,*pp2;
pp1=*name;
printf("ddd *name %s:\n",*(name+2));
pp2=*(name+n);
return((n<1||n>7)? pp1:pp2);
}
這裏的區別,是由於char與int打印不一樣造成,char打印,只需要給出地址即可,int必須給出具體的值。
另外,*name與name[0],是一樣的,就比如說:*(name+1)與name[1]等同。
注意:當指針數據當成一個實參,傳遞給函數形參時,此時形參相當於是char **,意義爲指向指針的指針,也就是雙重指針的意思,這裏的地址也就必須使用*name來表示指向第一個元素的內存位置。(可以想象的是,name數組中,是一個一個的地址,這些地址會指向一個一個的字符串,就是這個意思)
指向指針的指針:
main()
{static int a[5]={1,3,5,7,9};
int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};
int **p,i;
p=num;
for(i=0;i<5;i++)
{printf("%d\t",**p);p++;}
}
可以想象一個模型是,在num數組中有5個元素,每個元素都存放着一個指向int型元素的地址, 此時,p變量的值爲num的首地址,則*p的值爲num數組中的第一個元素的內容,也就是一個地址值,**p,就爲這個地址值所指向的int值。
當p++時,根據右結合定律,**p,會先去做一次*p,其實也就是*(*(p+1))的操作,這時,實際情況是,把p變量的值,設置爲num數組第二元素的地址。然後*(p+1)的內容就是num數組中,第二個元素(地址值)內容,**(p+1),就再交指向地址址所指的int值。