上一講已經(jīng)說(shuō)過(guò),指針是一種變量,它也有自己的地址,但由于它是專(zhuān)門(mén)用來(lái)存放地址的變量,所以把它認(rèn)為是種特殊的變量,既然有著特殊的身份,那么也理應(yīng)受到特殊的待遇,下面來(lái)看看它享受了那些優(yōu)待。
1.指針的定義
在C語(yǔ)言中,定義一個(gè)普通的變量(如整型數(shù)),我們這樣做:int i;而定義一個(gè)指針變量(指針)我們需要這樣做:int *p ; 還記得嗎,一個(gè)矩形中的值是有類(lèi)型的,可能是整型,可能是字符型……,它們?cè)臼恰扒灏住钡?,無(wú)類(lèi)型的,是我們通過(guò)一些手段使它們有了類(lèi)型。當(dāng)我們做出int i;這樣一個(gè)定義時(shí),編譯器就會(huì)分配一個(gè)地址(例如200)并和i關(guān)聯(lián)起來(lái),而int將限定編譯器把這個(gè)區(qū)域中的內(nèi)容作為整型數(shù)看待。
矩形內(nèi)的值被視為int型
現(xiàn)在我們又有了int *p;這個(gè)定義,假設(shè)p是指向變量i的(見(jiàn)下圖),p中存的是變量i的地址。*表示p是一個(gè)指針,而int表示p中所存的地址對(duì)應(yīng)的變量(即變量i)的類(lèi)型是int。
p指向i , int *p;中的int是指針p所指向的變量的類(lèi)型
我們將int稱(chēng)為指針p的基類(lèi)型,或指針p所指向的變量的類(lèi)型。
類(lèi)似地,我們可以有: char *s ; ( s是指向char型變量的指針)
float *f ; ( f是指向float型變量的指針)
double *d ; ( d是指向double型變量的指針)
由此得到聲明一個(gè)指針變量(指針)的一般形式 : 基類(lèi)型 * 指針名;
有一點(diǎn)要注意,在定義指針時(shí),以下兩種方式都是允許的,例如:
int *ptr1;
int* ptr2;
但一般比較傾向用第一種,因?yàn)榭梢员苊庖韵碌恼`解:
int* prt1, ptr2;
這樣的定義方式,容易使人誤以為ptr2也是一個(gè)指針,事實(shí)上并不是,prt2是一個(gè)int型變量,以下的定義方式中ptr1與ptr2才都是指針:
int* ptr1, *ptr2;
2.指針的運(yùn)算
<1>.&(address-of operator)取地址操作符:
究竟如何使一個(gè)指針指向一個(gè)變量呢?后面的語(yǔ)句給出了解答:int *p = &i;&用于取一個(gè)對(duì)象的地址(本文說(shuō)的對(duì)象是泛指的某一事物,如變量,數(shù)組等,和C++中的對(duì)象概念不同),這里用于將i的地址賦給p,那么指針p就指向了變量i。上述的語(yǔ)句也可以分開(kāi)寫(xiě),如:int *p; p = &i;
小擴(kuò)展:(下面大括號(hào)中的內(nèi)容,出涉指針的朋友可以跳過(guò),當(dāng)然也可以作為擴(kuò)展知識(shí))
{&的實(shí)質(zhì):當(dāng)對(duì)一個(gè)T類(lèi)型對(duì)象進(jìn)行& 操作時(shí),返回的是一個(gè)“指向T的指針”類(lèi)型的常量,即指針常量(pointer constant),在我們使用&運(yùn)算符時(shí)我們并不關(guān)心它是如何實(shí)現(xiàn)的,因?yàn)橛芯幾g器幫我們隱藏了這些細(xì)節(jié)。
可當(dāng)我們想要對(duì)一個(gè)指針賦一個(gè)絕對(duì)地址的時(shí)候,這個(gè)問(wèn)題就體現(xiàn)出來(lái)了,而且我們不得不去關(guān)注,在C語(yǔ)言中沒(méi)有一種內(nèi)建(built-in)的方法去表示指針常量,所以當(dāng)我們使用它的時(shí)候通常先寫(xiě)成整型常量的形式,然后再通過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換把它轉(zhuǎn)換成相應(yīng)的類(lèi)型,如:int * , double * , char *等。所以后面所示的做法是不行的: int *p = 0x12345678 ; 正確的方式應(yīng)為:int *p = (int *) 0x12345678;也許大家還記得我在第一講中說(shuō)的要注意指針中只能存放地址,不能將一個(gè)非0值整型常量表達(dá)式或者其他非地址類(lèi)型的數(shù)據(jù)賦給一個(gè)指針,原因就在此。在大多數(shù)計(jì)算機(jī)中,內(nèi)存地址確實(shí)是以無(wú)符號(hào)整型數(shù)來(lái)表示的,而且多以16進(jìn)制表示,但我們?cè)?/span>C語(yǔ)言中不能用整型數(shù)去表示地址,只能用指針常量來(lái)表示,因?yàn)樗潜挥脕?lái)賦給一個(gè)指針的。
對(duì)于這個(gè)賦值問(wèn)題還可以換一個(gè)角度去理解,在C語(yǔ)言中,使用賦值操作符時(shí),賦值操作符左邊和右邊的表達(dá)式類(lèi)型應(yīng)該是相同的,如果不是,賦值操作符將試圖把右邊表達(dá)式的值轉(zhuǎn)換為左邊的類(lèi)型。所以如果寫(xiě)出int *p = 0x12345678 ; 這條語(yǔ)句編譯器會(huì)報(bào)錯(cuò):'=' : cannot convert from ' const int ' to ' int* ',因?yàn)橘x值操作符左邊和右邊的表達(dá)式的類(lèi)型應(yīng)該相同,而0x12345678是int型常量,p是一個(gè)指向int型的指針,兩者類(lèi)型不同,所以正確的方式是:int *p = (int *) 0x12345678 ; }
<2>.* (Dereference operator)解引用操作符
* 在定義時(shí)用來(lái)說(shuō)明一個(gè)變量是指針,而在定義了一個(gè)指針之后,我們使用(引用)指針時(shí),*p表示的是p所指向的對(duì)象(即i)。也就是說(shuō),對(duì)于一個(gè)已定義的指針使用 * 操作符,將訪問(wèn)這個(gè)指針?biāo)赶虻膶?duì)象,我們來(lái)看下面的程序:
#include<stdio.h>
int main()
{
int i; /*定義一個(gè)int型變量i*/
int *p; /* 定義一個(gè)指向int類(lèi)型的指針p */
i = 2 ; /*初始化i為2 */
p = &i ; /*將i的地址賦給p,即使p指向i */
printf("%d\n", i ) ; /*輸出i的值*/
printf("%d\n", *p ) ; /* 輸出p所指向的存儲(chǔ)單元的值,即i的值*/
return 0 ; /*標(biāo)準(zhǔn)C語(yǔ)言主函數(shù)應(yīng)返回一個(gè)值,用以通知操作系統(tǒng)程序執(zhí)行成功與否,通常0表示成功*/
}
程序輸出結(jié)果為:
2
2
對(duì)于 * 操作符,由于它有兩個(gè)等價(jià)的術(shù)語(yǔ)dereference和indirection,所以在國(guó)內(nèi)的書(shū)籍中你會(huì)看到各種翻譯方法,如:解引用、解除引用、反引用、反向引用、間接引用、間接訪問(wèn)……
只要你知道它是用來(lái)訪問(wèn)一個(gè)指針?biāo)赶虻膶?duì)象的,那么不管它叫什么都不重要了。還是那句話,弄懂是什么,不要在乎叫什么,如果你理解了它的真正含義,大可以簡(jiǎn)潔地稱(chēng)它為“星號(hào)”操作符!
3.指針的初始化
ANSI C定義了零指針常量的概念:一個(gè)具有0值的整形常量表達(dá)式,或者此類(lèi)表達(dá)式被強(qiáng)制轉(zhuǎn)換為void *類(lèi)型,則稱(chēng)為空指針常量,它可以用來(lái)初始化或賦給任何類(lèi)型的指針。也就是說(shuō),我們可以將0、0L、'/0'、2–2、0*5以及(void*)0賦給一個(gè)任何類(lèi)型的指針,此后這個(gè)指針就成為一個(gè)空指針,由系統(tǒng)保證空指針不指向任何對(duì)象或函數(shù)。
ANSI C還定義了一個(gè)宏NULL,用來(lái)表示空指針常量。大多數(shù)C語(yǔ)言的實(shí)現(xiàn)中NULL是采用后面這種方式定義的:#define NULL ((void*)0)。
對(duì)指針進(jìn)行初始化時(shí)常用的有以下幾種方式:
1.采用NULL或空指針常量,如:int *p = NULL;或char *p = 2-2; 或float*p = 0;
2.取一個(gè)對(duì)象的地址然后賦給一個(gè)指針,如:int i = 3; int *ip = &i;
3.將一個(gè)指針常量賦給一個(gè)指針,如:long *p = (long *)0xfffffff0;
4.將一個(gè)T類(lèi)型數(shù)組的名字賦給一個(gè)相同類(lèi)型的指針,如:char ary[100]; char *cp = ary;
5.將一個(gè)指針的地址賦給一個(gè)指針,如:int i = 3; int *ip = &i;int **pp = &ip;
6.將一個(gè)字符串常量賦給一個(gè)字符指針,如:char *cp = “abcdefg”;
對(duì)指針進(jìn)行初始化或賦值的實(shí)質(zhì)是將一個(gè)地址或同類(lèi)型(或相兼容的類(lèi)型)的指針賦給它,而不管這個(gè)地址是怎么取得的。要注意的是:對(duì)于一個(gè)不確定要指向何種類(lèi)型的指針,在定義它之后最好把它初始化為NULL,并在解引用這個(gè)指針時(shí)對(duì)它進(jìn)行檢驗(yàn),防止解引用空指針。另外,為程序中任何新創(chuàng)建的變量提供一個(gè)合法的初始值是一個(gè)好習(xí)慣,它可以幫你避免一些不必要的麻煩。
4.void *型指針
ANSI C定義了一種void *型指針,表示定義一個(gè)指針,但不指定它指向何種類(lèi)型的數(shù)據(jù)。void *型指針作為一種通用的指針,可以和其它任何類(lèi)型的指針(函數(shù)指針除外)相互轉(zhuǎn)化而不需要類(lèi)型強(qiáng)制轉(zhuǎn)換,但不能對(duì)它進(jìn)行解引用及下標(biāo)操作。C語(yǔ)言中的malloc函數(shù)的返回值就是一個(gè)void *型指針,我們可以把它直接賦給一個(gè)其他類(lèi)型的指針,但從安全的編程風(fēng)格角度以及兼容性上講,最好還是將返回的指針強(qiáng)制轉(zhuǎn)換為所需的類(lèi)型,另外,malloc在無(wú)法滿足請(qǐng)求時(shí)會(huì)通過(guò)返回一個(gè)空指針來(lái)作為“內(nèi)存分配失敗”的信號(hào),所以要注意返回值指針的判空。
5.指向指針的指針
在指針初始化的第5種方式中提到了用一個(gè)指針的地址來(lái)初始化一個(gè)指針?;貞浺幌律弦恢v的內(nèi)容:指針是一種變量,它也有自己的地址,所以它本身也是可用指針指向的對(duì)象。我們可以將指針的地址存放在另一個(gè)指針中,如:
int i = 5000;
int *pi = &i;
int **ppi =π
此時(shí)的ppi即是一個(gè)指向指針的指針,下圖表示了這些對(duì)象:
i的地址為108,pi的內(nèi)容就是i的地址,而pi的地址為104,ppi的內(nèi)容即是pi的地址。對(duì)ppi解引用照常會(huì)得到ppi所指的對(duì)象,所獲得的對(duì)象是指向int型變量的指針pi。想要真正地訪問(wèn)到i.,必須對(duì)ppi進(jìn)行兩次解引用,如下面代碼所示:
printf("%d\n", i );
printf("%d\n", *pi );
printf("%d\n", **ppi );
以上三條語(yǔ)句的輸出均為5000。
聯(lián)系客服