?? 10.txt
字號:
前面說過,當(dāng)一個指針變量在未取得確定地址前使用是危險(xiǎn)的,容易引起錯誤。但是對指針變量直接賦值是可以的。因?yàn)镃系統(tǒng)對指針變量賦值時要給以確定的地址。
因此,
char *ps="C Langage";
或者
char *ps;
ps="C Language";
都是合法的。
10.5 函數(shù)指針變量
在C語言中,一個函數(shù)總是占用一段連續(xù)的內(nèi)存區(qū),而函數(shù)名就是該函數(shù)所占內(nèi)存區(qū)的首地址。我們可以把函數(shù)的這個首地址(或稱入口地址)賦予一個指針變量,使該指針變量指向該函數(shù)。然后通過指針變量就可以找到并調(diào)用這個函數(shù)。我們把這種指向函數(shù)的指針變量稱為“函數(shù)指針變量”。
函數(shù)指針變量定義的一般形式為:
類型說明符 (*指針變量名)();
其中“類型說明符”表示被指函數(shù)的返回值的類型。“(* 指針變量名)”表示“*”后面的變量是定義的指針變量。最后的空括號表示指針變量所指的是一個函數(shù)。
例如:
int (*pf)();
表示pf是一個指向函數(shù)入口的指針變量,該函數(shù)的返回值(函數(shù)值)是整型。
【例10.31】本例用來說明用指針形式實(shí)現(xiàn)對函數(shù)調(diào)用的方法。
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);
}
從上述程序可以看出用,函數(shù)指針變量形式調(diào)用函數(shù)的步驟如下:
1) 先定義函數(shù)指針變量,如后一程序中第9行 int (*pmax)();定義 pmax為函數(shù)指針變量。
2) 把被調(diào)函數(shù)的入口地址(函數(shù)名)賦予該函數(shù)指針變量,如程序中第11行 pmax=max;
3) 用函數(shù)指針變量形式調(diào)用函數(shù),如程序第14行 z=(*pmax)(x,y);
4) 調(diào)用函數(shù)的一般形式為:
(*指針變量名) (實(shí)參表)
使用函數(shù)指針變量還應(yīng)注意以下兩點(diǎn):
a) 函數(shù)指針變量不能進(jìn)行算術(shù)運(yùn)算,這是與數(shù)組指針變量不同的。數(shù)組指針變量加減一個整數(shù)可使指針移動指向后面或前面的數(shù)組元素,而函數(shù)指針的移動是毫無意義的。
b) 函數(shù)調(diào)用中"(*指針變量名)"的兩邊的括號不可少,其中的*不應(yīng)該理解為求值運(yùn)算,在此處它只是一種表示符號。
10.6 指針型函數(shù)
前面我們介紹過,所謂函數(shù)類型是指函數(shù)返回值的類型。在C語言中允許一個函數(shù)的返回值是一個指針(即地址),這種返回指針值的函數(shù)稱為指針型函數(shù)。
定義指針型函數(shù)的一般形式為:
類型說明符 *函數(shù)名(形參表)
{
…… /*函數(shù)體*/
}
其中函數(shù)名之前加了“*”號表明這是一個指針型函數(shù),即返回值是一個指針。類型說明符表示了返回的指針值所指向的數(shù)據(jù)類型。
如:
int *ap(int x,int y)
{
...... /*函數(shù)體*/
}
表示ap是一個返回指針值的指針型函數(shù),它返回的指針指向一個整型變量。
【例10.32】本程序是通過指針函數(shù),輸入一個1~7之間的整數(shù),輸出對應(yīng)的星期名。
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]);
}
本例中定義了一個指針型函數(shù)day_name,它的返回值指向一個字符串。該函數(shù)中定義了一個靜態(tài)指針數(shù)組name。name數(shù)組初始化賦值為八個字符串,分別表示各個星期名及出錯提示。形參n表示與星期名所對應(yīng)的整數(shù)。在主函數(shù)中,把輸入的整數(shù)i作為實(shí)參,在printf語句中調(diào)用day_name函數(shù)并把i值傳送給形參n。day_name函數(shù)中的return語句包含一個條件表達(dá)式,n值若大于7或小于1則把name[0]指針返回主函數(shù)輸出出錯提示字符串“Illegal day”。否則返回主函數(shù)輸出對應(yīng)的星期名。主函數(shù)中的第7行是個條件語句,其語義是,如輸入為負(fù)數(shù)(i<0)則中止程序運(yùn)行退出程序。exit是一個庫函數(shù),exit(1)表示發(fā)生錯誤后退出程序,exit(0)表示正常退出。
應(yīng)該特別注意的是函數(shù)指針變量和指針型函數(shù)這兩者在寫法和意義上的區(qū)別。如int(*p)()和int *p()是兩個完全不同的量。
int (*p)()是一個變量說明,說明p是一個指向函數(shù)入口的指針變量,該函數(shù)的返回值是整型量,(*p)的兩邊的括號不能少。
int *p()則不是變量說明而是函數(shù)說明,說明p是一個指針型函數(shù),其返回值是一個指向整型量的指針,*p兩邊沒有括號。作為函數(shù)說明,在括號內(nèi)最好寫入形式參數(shù),這樣便于與變量說明區(qū)別。
對于指針型函數(shù)定義,int *p()只是函數(shù)頭部分,一般還應(yīng)該有函數(shù)體部分。
10.7 指針數(shù)組和指向指針的指針
10.7.1 指針數(shù)組的概念
一個數(shù)組的元素值為指針則是指針數(shù)組。 指針數(shù)組是一組有序的指針的集合。 指針數(shù)組的所有元素都必須是具有相同存儲類型和指向相同數(shù)據(jù)類型的指針變量。
指針數(shù)組說明的一般形式為:
類型說明符 *數(shù)組名[數(shù)組長度]
其中類型說明符為指針值所指向的變量的類型。
例如:
int *pa[3]
表示pa是一個指針數(shù)組,它有三個數(shù)組元素,每個元素值都是一個指針,指向整型變量。
【例10.33】通常可用一個指針數(shù)組來指向一個二維數(shù)組。指針數(shù)組中的每個元素被賦予二維數(shù)組每一行的首地址,因此也可理解為指向一個一維數(shù)組。
main(){
int a[3][3]={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));
for(i=0;i<3;i++)
printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));
}
本例程序中,pa是一個指針數(shù)組,三個元素分別指向二維數(shù)組a的各行。然后用循環(huán)語句輸出指定的數(shù)組元素。其中*a[i]表示i行0列元素值;*(*(a+i)+i)表示i行i列的元素值;*pa[i]表示i行0列元素值;由于p與a[0]相同,故p[i]表示0行i列的值;*(p+i)表示0行i列的值。讀者可仔細(xì)領(lǐng)會元素值的各種不同的表示方法。
應(yīng)該注意指針數(shù)組和二維數(shù)組指針變量的區(qū)別。這兩者雖然都可用來表示二維數(shù)組,但是其表示方法和意義是不同的。
二維數(shù)組指針變量是單個的變量,其一般形式中"(*指針變量名)"兩邊的括號不可少。而指針數(shù)組類型表示的是多個指針(一組有序指針)在一般形式中"*指針數(shù)組名"兩邊不能有括號。
例如:
int (*p)[3];
表示一個指向二維數(shù)組的指針變量。該二維數(shù)組的列數(shù)為3或分解為一維數(shù)組的長度為3。
int *p[3]
表示p是一個指針數(shù)組,有三個下標(biāo)變量p[0],p[1],p[2]均為指針變量。
指針數(shù)組也常用來表示一組字符串,這時指針數(shù)組的每個元素被賦予一個字符串的首地址。指向字符串的指針數(shù)組的初始化更為簡單。例如在例10.32中即采用指針數(shù)組來表示一組字符串。其初始化賦值為:
char *name[]={"Illagal day",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"};
完成這個初始化賦值之后,name[0]即指向字符串"Illegal day",name[1]指向"Monday"......。
指針數(shù)組也可以用作函數(shù)參數(shù)。
【例10.34】指針數(shù)組作指針型函數(shù)的參數(shù)。在本例主函數(shù)中,定義了一個指針數(shù)組name,并對name 作了初始化賦值。其每個元素都指向一個字符串。然后又以name作為實(shí)參調(diào)用指針型函數(shù)day_name,在調(diào)用時把數(shù)組名name賦予形參變量name,輸入的整數(shù)i作為第二個實(shí)參賦予形參n。在day_ name函數(shù)中定義了兩個指針變量pp1和pp2,pp1被賦予name[0]的值(即*name),pp2被賦予name[n]的值即*(name+ n)。由條件表達(dá)式?jīng)Q定返回pp1或pp2指針給主函數(shù)中的指針變量ps。最后輸出i和ps的值。
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);
if(i<0) exit(1);
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;
pp2=*(name+n);
return((n<1||n>7)? pp1:pp2);
}
【例10.35】輸入5個國名并按字母順序排列后輸出。現(xiàn)編程如下:
#include"string.h"
main(){
void sort(char *name[],int n);
void print(char *name[],int n);
static char *name[]={ "CHINA","AMERICA","AUSTRALIA",
"FRANCE","GERMAN"};
int n=5;
sort(name,n);
print(name,n);
}
void sort(char *name[],int n){
char *pt;
int i,j,k;
for(i=0;i<n-1;i++){
k=i;
for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if(k!=i){
pt=name[i];
name[i]=name[k];
name[k]=pt;
}
}
}
void print(char *name[],int n){
int i;
for (i=0;i<n;i++) printf("%s\n",name[i]);
}
說明:
在以前的例子中采用了普通的排序方法,逐個比較之后交換字符串的位置。交換字符串的物理位置是通過字符串復(fù)制函數(shù)完成的。反復(fù)的交換將使程序執(zhí)行的速度很慢,同時由于各字符串(國名)的長度不同,又增加了存儲管理的負(fù)擔(dān)。用指針數(shù)組能很好地解決這些問題。把所有的字符串存放在一個數(shù)組中,把這些字符數(shù)組的首地址放在一個指針數(shù)組中,當(dāng)需要交換兩個字符串時,只須交換指針數(shù)組相應(yīng)兩元素的內(nèi)容(地址)即可,而不必交換字符串本身。
本程序定義了兩個函數(shù),一個名為sort完成排序,其形參為指針數(shù)組name,即為待排序的各字符串?dāng)?shù)組的指針。形參n為字符串的個數(shù)。另一個函數(shù)名為print,用于排序后字符串的輸出,其形參與sort的形參相同。主函數(shù)main中,定義了指針數(shù)組name 并作了初始化賦值。然后分別調(diào)用sort函數(shù)和print函數(shù)完成排序和輸出。值得說明的是在sort函數(shù)中,對兩個字符串比較,采用了strcmp函數(shù),strcmp函數(shù)允許參與比較的字符串以指針方式出現(xiàn)。name[k]和name[j]均為指針,因此是合法的。字符串比較后需要交換時,只交換指針數(shù)組元素的值,而不交換具體的字符串,這樣將大大減少時間的開銷,提高了運(yùn)行效率。
10.7.2 指向指針的指針
如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的指針變量。
在前面已經(jīng)介紹過,通過指針訪問變量稱為間接訪問。由于指針變量直接指向變量,所以稱為“單級間址”。而如果通過指向指針的指針變量來訪問變量則構(gòu)成“二級間址”。
從下圖可以看到,name是一個指針數(shù)組,它的每一個元素是一個指針型數(shù)據(jù),其值為地址。Name是一個數(shù)據(jù),它的每一個元素都有相應(yīng)的地址。數(shù)組名name代表該指針數(shù)組的首地址。name+1是mane[i]的地址。name+1就是指向指針型數(shù)據(jù)的指針(地址)。還可以設(shè)置一個指針變量p,使它指向指針數(shù)組元素。P就是指向指針型數(shù)據(jù)的指針變量。
怎樣定義一個指向指針型數(shù)據(jù)的指針變量呢?如下:
char **p;
p前面有兩個*號,相當(dāng)于*(*p)。顯然*p是指針變量的定義形式,如果沒有最前面的*,那就是定義了一個指向字符數(shù)據(jù)的指針變量。現(xiàn)在它前面又有一個*號,表示指針變量p是指向一個字符指針型變量的。*p就是p所指向的另一個指針變量。
從下圖可以看到,name是一個指針數(shù)組,它的每一個元素是一個指針型數(shù)據(jù),其值為地址。name是一個數(shù)組,它的每一個元素都有相應(yīng)的地址。數(shù)組名name代表該指針數(shù)組的首地址。name+1是mane[i]的地址。name+1就是指向指針型數(shù)據(jù)的指針(地址)。還可以設(shè)置一個指針變量p,使它指向指針數(shù)組元素。P就是指向指針型數(shù)據(jù)的指針變量。
如果有:
p=name+2;
printf(“%o\n”,*p);
printf(“%s\n”,*p);
則,第一個printf函數(shù)語句輸出name[2]的值(它是一個地址),第二個printf函數(shù)語句以字符串形式(%s)輸出字符串“Great Wall”。
【例10.36】使用指向指針的指針。
main()
{char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer desighn"};
char **p;
int i;
for(i=0;i<5;i++)
{p=name+i;
printf("%s\n",*p);
}
}
說明:
p是指向指針的指針變量。
【例10.37】一個指針數(shù)組的元素指向數(shù)據(jù)的簡單例子。
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++;}
}
說明:
指針數(shù)組的元素只能存放地址。
10.7.3 main函數(shù)的參數(shù)
前面介紹的main函數(shù)都是不帶參數(shù)的。因此main 后的括號都是空括號。實(shí)際上,main函數(shù)可以帶參數(shù),這個參數(shù)可以認(rèn)為是 main函數(shù)的形式參數(shù)。C語言規(guī)定main函數(shù)的參數(shù)只能有兩個,習(xí)慣上這兩個參數(shù)寫為argc和argv。因此,main函數(shù)的函數(shù)頭可寫為:
main (argc,argv)
C語言還規(guī)定argc(第一個形參)必須是整型變量,argv( 第二個形參)必須是指向字符串的指針數(shù)組。加上形參說明后,main函數(shù)的函數(shù)頭應(yīng)寫為:
main (int argc,char *argv[])
由于main函數(shù)不能被其它函數(shù)調(diào)用,因此不可能在程序內(nèi)部取得實(shí)際值。那么,在何處把實(shí)參值賦予main函數(shù)的形參呢? 實(shí)際上,main函數(shù)的參數(shù)值是從操作系統(tǒng)命令行上獲得的。當(dāng)我們要運(yùn)行一個可執(zhí)行文件時,在DOS提示符下鍵入文件名,再輸入實(shí)際參數(shù)即可把這些實(shí)參傳送到main的形參中去。
DOS提示符下命令行的一般形式為:
C:\>可執(zhí)行文件名 參數(shù) 參數(shù)……;
但是應(yīng)該特別注意的是,main 的兩個形參和命令行中的參數(shù)在位置上不是一一對應(yīng)的。因?yàn)?main的形參只有二個,而命令行中的參數(shù)個數(shù)原則上未加限制。argc參數(shù)表示了命令行中參數(shù)的個數(shù)(注意:文件名本身也算一個參數(shù)),argc的值是在輸入命令行時由系統(tǒng)按實(shí)際參數(shù)的個數(shù)自動賦予的。
例如有命令行為:
C:\>E24 BASIC foxpro FORTRAN
由于文件名E24本身也算一個參數(shù),所以共有4個參數(shù),因此argc取得的值為4。argv參數(shù)是字符串指針數(shù)組,其各元素值為命令行中各字符串(參數(shù)均按字符串處理)的首地址。 指針數(shù)組的長度即為參數(shù)個數(shù)。數(shù)組元素初值由系統(tǒng)自動賦予。其表示如圖所示:
【例10.38】
main(int argc,char *argv){
while(argc-->1)
printf("%s\n",*++argv);
}
本例是顯示命令行中輸入的參數(shù)。如果上例的可執(zhí)行文件名為e24.exe,存放在A驅(qū)動器的盤內(nèi)。因此輸入的命令行為:
C:\>a:e24 BASIC foxpro FORTRAN
則運(yùn)行結(jié)果為:
BASIC
foxpro
FORTRAN
該行共有4個參數(shù),執(zhí)行main時,argc的初值即為4。argv的4個元素分為4個字符串的首地址。執(zhí)行while語句,每循環(huán)一次argv值減1,當(dāng)argv等于1時停止循環(huán),共循環(huán)三次,因此共可輸出三個參數(shù)。在printf函數(shù)中,由于打印項(xiàng)*++argv是先加1再打印, 故第一次打印的是argv[1]所指的字符串BASIC。第二、三次循環(huán)分別打印后二個字符串。而參數(shù)e24是文件名,不必輸出。
10.8 有關(guān)指針的數(shù)據(jù)類型和指針運(yùn)算的小結(jié)
10.8.1 有關(guān)指針的數(shù)據(jù)類型的小結(jié)
定義 含 義
int i; 定義整型變量i
int *p p為指向整型數(shù)據(jù)的指針變量
int a[n]; 定義整型數(shù)組a,它有n個元素
int *p[n]; 定義指針數(shù)組p,它由n個指向整型數(shù)據(jù)的指針元素組成
int (*p)[n]; p為指向含n個元素的一維數(shù)組的指針變量
int f(); f為帶回整型函數(shù)值的函數(shù)
int *p(); p為帶回一個指針的函數(shù),該指針指向整型數(shù)據(jù)
int (*p)(); p為指向函數(shù)的指針,該函數(shù)返回一個整型值
int **p; P是一個指針變量,它指向一個指向整型數(shù)據(jù)的指針變量
10.8.2 指針運(yùn)算的小結(jié)
現(xiàn)把全部指針運(yùn)算列出如下:
1) 指針變量加(減)一個整數(shù):
例如:p++、p--、p+i、p-i、p+=i、p-=i
一個指針變量加(減)一個整數(shù)并不是簡單地將原值加(減)一個整數(shù),而是將該指針變量的原值(是一個地址)和它指向的變量所占用的內(nèi)存單元字節(jié)數(shù)加(減)。
2) 指針變量賦值:將一個變量的地址賦給一個指針變量。
p=&a; (將變量a的地址賦給p)
p=array; (將數(shù)組array的首地址賦給p)
p=&array[i]; (將數(shù)組array第i個元素的地址賦給p)
p=max; (max為已定義的函數(shù),將max的入口地址賦給p)
p1=p2; (p1和p2都是指針變量,將p2的值賦給p1)
注意:不能如下:
p=1000;
3) 指針變量可以有空值,即該指針變量不指向任何變量:
p=NULL;
4) 兩個指針變量可以相減:如果兩個指針變量指向同一個數(shù)組的元素,則兩個指針變量值之差是兩個指針之間的元素個數(shù)。
5) 兩個指針變量比較:如果兩個指針變量指向同一個數(shù)組的元素,則兩個指針變量可以進(jìn)行比較。指向前面的元素的指針變量“小于” 指向后面的元素的指針變量。
10.8.3 void指針類型
ANSI新標(biāo)準(zhǔn)增加了一種“void”指針類型,即可以定義一個指針變量,但不指定它是指向哪一種類型數(shù)據(jù)。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -