全面掌握C語(yǔ)言之指針,需學(xué)會(huì)這3個(gè)關(guān)鍵點(diǎn)


前言
上一章節(jié)給大家介紹了C語(yǔ)言數(shù)組,對(duì)于數(shù)組不是很了解的可以回顧學(xué)習(xí)下。本章節(jié)主要是給

什么是指針
C語(yǔ)言里,變量存放在內(nèi)存中,而內(nèi)存其實(shí)就是一組有序字節(jié)組成的數(shù)組,每個(gè)字節(jié)有唯一的內(nèi)存地址。CPU 通過(guò)內(nèi)存尋址對(duì)存儲(chǔ)在內(nèi)存中的某個(gè)指定數(shù)據(jù)對(duì)象的地址進(jìn)行定位。這里,數(shù)據(jù)對(duì)象是指存儲(chǔ)在內(nèi)存中的一個(gè)指定數(shù)據(jù)類(lèi)型的數(shù)值或字符串,它們都有一個(gè)自己的地址,而指針便是保存這個(gè)地址的變量。也就是說(shuō):指針是一種保存變量地址的變量。
前面已經(jīng)提到內(nèi)存其實(shí)就是一組有序字節(jié)組成的數(shù)組,數(shù)組中,每個(gè)字節(jié)大大小固定,都是 8bit。對(duì)這些連續(xù)的字節(jié)從 0 開(kāi)始進(jìn)行編號(hào),每個(gè)字節(jié)都有唯一的一個(gè)編號(hào),這個(gè)編號(hào)就是內(nèi)存地址。示意如下圖:

這是一個(gè) 4GB 的內(nèi)存,可以存放 2^32 個(gè)字節(jié)的數(shù)據(jù)。左側(cè)的連續(xù)的十六進(jìn)制編號(hào)就是內(nèi)存地址,每個(gè)內(nèi)存地址對(duì)應(yīng)一個(gè)字節(jié)的內(nèi)存空間。而指針變量保存的就是這個(gè)編號(hào),也即內(nèi)存地址。

為什么使用指針
在C語(yǔ)言中,指針的使用非常廣泛,因?yàn)槭褂弥羔樛梢陨筛咝А⒏o湊的代碼。總的來(lái)說(shuō),使用指針有如下好處:
指針的使用使得不同區(qū)域的代碼可以輕易
的 共享內(nèi)存數(shù)據(jù),這樣可以使程序更為快速高效;
C語(yǔ)言中一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu)往往需要使用指針來(lái)構(gòu)建,如鏈表、二叉樹(shù)等;
C語(yǔ)言是傳值調(diào)用,而有些操作傳值調(diào)用是無(wú)法完成的,如通過(guò)被調(diào)函數(shù)修改調(diào)用函數(shù)的對(duì)象,但是這種操作可以由指針來(lái)完成,而且并不違背傳值調(diào)用。

如何聲明指針變量
基本語(yǔ)法:

指針的類(lèi)型
從語(yǔ)法的角度看,你只要把指針聲明語(yǔ)句里的指針名字去掉,剩下的部分就是這個(gè)指針的類(lèi)型。這是指針本身所具有的類(lèi)型。讓我們看看例一中各個(gè)指針的類(lèi)型:
int*ptr;//指針的類(lèi)型是int*
char*ptr;//指針的類(lèi)型是char*
int**ptr;//指針的類(lèi)型是int**
int(*ptr)[3];//指針的類(lèi)型是int(*)[3]
int*(*ptr)[4];//指針的類(lèi)型是int*(*)[4]
怎么樣?找出指針的類(lèi)型的方法是不是很簡(jiǎn)單?
指針?biāo)赶虻念?lèi)型
當(dāng)你通過(guò)指針來(lái)訪問(wèn)指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念?lèi)型決定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來(lái)看待。從語(yǔ)法上看,你只須把指針聲明語(yǔ)句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針?biāo)赶虻念?lèi)型。例如:
int*ptr; //指針?biāo)赶虻念?lèi)型是int
char*ptr; //指針?biāo)赶虻念?lèi)型是char
int**ptr; //指針?biāo)赶虻念?lèi)型是int*
int(*ptr)[3]; //指針?biāo)赶虻念?lèi)型是int()[3]
int*(*ptr)[4];//指針?biāo)赶虻念?lèi)型是int*()[4]
指針的類(lèi)型(即指針本身的類(lèi)型)和指針?biāo)赶虻念?lèi)型是兩個(gè)概念。當(dāng)你對(duì)C 越來(lái)越熟悉時(shí),你會(huì)發(fā)現(xiàn),把與指針攪和在一起的"類(lèi)型"這個(gè)概念分成"指針的類(lèi)型"和"指針?biāo)赶虻念?lèi)型"兩個(gè)概念,是精通指針的關(guān)鍵點(diǎn)之一。并且在指針的運(yùn)算中,這兩種類(lèi)型的保持一致尤為重要。
以后,每遇到一個(gè)指針,都應(yīng)該問(wèn)問(wèn):這個(gè)指針的類(lèi)型是什么?指針指的類(lèi)型是什么?該指針指向了哪里?

指針運(yùn)算
C 指針的算術(shù)運(yùn)算只限于兩種形式:
指針+n 或者指針-n
可以對(duì)指針變量 p 進(jìn)行 p++、p--、p + i 等操作,所得結(jié)果也是一個(gè)指針,只是指針?biāo)赶虻膬?nèi)存地址相比于 p 所指的內(nèi)存地址前進(jìn)或者后退了 i 個(gè)操作數(shù)。用一張圖來(lái)說(shuō)明一下:

在上圖中,10000000等是內(nèi)存地址的十六進(jìn)制表示(數(shù)值是假定的),p 是一個(gè) int 類(lèi)型的指針,指向內(nèi)存地址 0x10000008 處。則 p++ 將指向與 p 相鄰的下一個(gè)內(nèi)存地址,由于 int 型數(shù)據(jù)占 4 個(gè)字節(jié),因此 p++ 所指的內(nèi)存地址為 1000000b。其余類(lèi)推。不過(guò)要注意的是,這種運(yùn)算并不會(huì)改變指針變量 p 自身的地址,只是改變了它所指向的地址。舉個(gè)例子:
指針 - 指針
只有當(dāng)兩個(gè)指針都指向同一個(gè)數(shù)組中的元素時(shí),才允許從一個(gè)指針減去另一個(gè)指針。兩個(gè)指針相減的結(jié)果的類(lèi)型是 ptrdiff_t,它是一種有符號(hào)整數(shù)類(lèi)型。減法運(yùn)算的值是兩個(gè)指針在內(nèi)存中的距離(以數(shù)組元素的長(zhǎng)度為單位,而不是以字節(jié)為單位),因?yàn)闇p法運(yùn)算的結(jié)果將除以數(shù)組元素類(lèi)型的長(zhǎng)度。舉個(gè)例子:


其他內(nèi)容
NULL指針
NULL 指針是一個(gè)特殊的指針變量,表示不指向任何東西。可以通過(guò)給一個(gè)指針賦一個(gè)零值來(lái)生成一個(gè) NULL 指針。在大多數(shù)的操作系統(tǒng)上,程序不允許訪問(wèn)地址為 0 的內(nèi)存,因?yàn)樵搩?nèi)存是為操作系統(tǒng)保留的。但是,內(nèi)存地址 0 有一個(gè)特別重要的意義,它表明該指針不指向一個(gè)可訪問(wèn)的內(nèi)存位置。
void*類(lèi)型指針
void*類(lèi)型指針也稱(chēng)之為萬(wàn)能指針,可以指向任何類(lèi)型的數(shù)據(jù)的地址,但是再使用前必須強(qiáng)制類(lèi)型轉(zhuǎn)換!!切記,如下測(cè)試代碼:

指針與數(shù)組
在C語(yǔ)言中,指針與數(shù)組之間的關(guān)系十分密切。實(shí)際上,許多可以用數(shù)組完成的工作都可以使用指針來(lái)完成。一般來(lái)說(shuō),用指針編寫(xiě)的程序比用數(shù)組編寫(xiě)的程序執(zhí)行速度快,但另一方面,用指針實(shí)現(xiàn)的程序理解起來(lái)稍微困難一些。
指針數(shù)組
指針是一個(gè)變量,而數(shù)組是用于存儲(chǔ)變量的容器,因此,指針也可以像其他變量一樣存儲(chǔ)在數(shù)組中,也就是指針數(shù)組。指針數(shù)組是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素都是指針。聲明一個(gè)指針數(shù)組的方法如下:
int *p[10]; // 聲明一個(gè)指針數(shù)組,該數(shù)組有10個(gè)元素,其中每個(gè)元素都是一個(gè)指向int類(lèi)型的指針
在上述聲明中,由于 [] 的優(yōu)先級(jí)比 * 高,故 p 先與 [] 結(jié)合,成為一個(gè)數(shù)組 p[];再由 int * 指明這是一個(gè) int 類(lèi)型的指針數(shù)組,數(shù)組中的元素都是 int 類(lèi)型的指針。數(shù)組的第 i 個(gè)元素是 *p[i],而 p[i] 是一個(gè)指針。由于指針數(shù)組中存放著多個(gè)指針,操作靈活,在一些需要操作大量數(shù)據(jù)的程序中使用,可以使程序更靈活快速。
數(shù)組指針
數(shù)組指針是一個(gè)指針,它指向一個(gè)數(shù)組。聲明一個(gè)數(shù)組指針的方法如下:
int (*p)[10]; // 聲明一個(gè)數(shù)組指針 p ,該指針指向一個(gè)數(shù)組。由于 () 的優(yōu)先級(jí)最高,所以 p 是一個(gè)指針,指向一個(gè) int 類(lèi)型的一維數(shù)組,這個(gè)一維數(shù)組的長(zhǎng)度是 10,這也是指針 p 的步長(zhǎng)。也就是說(shuō),執(zhí)行 p+1 時(shí),p 要跨過(guò) n 個(gè) int 型數(shù)據(jù)的長(zhǎng)度。數(shù)組指針與二維數(shù)組聯(lián)系密切,可以用數(shù)組指針來(lái)指向一個(gè)二維數(shù)組。

指針與函數(shù)
C語(yǔ)言的所有參數(shù)均是以“傳值調(diào)用”的方式進(jìn)行傳遞的,這意味著函數(shù)將獲得參數(shù)值的一份拷貝。這樣,函數(shù)可以放心修改這個(gè)拷貝值,而不必?fù)?dān)心會(huì)修改調(diào)用程序?qū)嶋H傳遞給它的參數(shù)。
指針作為函數(shù)的參數(shù)
傳值調(diào)用的好處是是被調(diào)函數(shù)不會(huì)改變調(diào)用函數(shù)傳過(guò)來(lái)的值,可以放心修改。但是有時(shí)候需要被調(diào)函數(shù)回傳一個(gè)值給調(diào)用函數(shù),這樣的話(huà),傳值調(diào)用就無(wú)法做到。為了解決這個(gè)問(wèn)題,可以使用傳指針調(diào)用。指針參數(shù)使得被調(diào)函數(shù)能夠訪問(wèn)和修改主調(diào)函數(shù)中對(duì)象的值。

函數(shù)指針:指向函數(shù)的指針
在C語(yǔ)言中,函數(shù)本身不是變量,但是可以定義指向函數(shù)的指針,也稱(chēng)作函數(shù)指針,函數(shù)指針指向函數(shù)的入口地址。這種類(lèi)型的指針可以被賦值、存放在數(shù)組中、傳遞給函數(shù)以及作為函數(shù)的返回值等等。聲明一個(gè)函數(shù)指針的方法如下:
返回值類(lèi)型 (* 指針變量名)([形參列表]); int (*pointer)(int *,int *); // 聲明一個(gè)函數(shù)指針
上述代碼聲明了一個(gè)函數(shù)指針 pointer ,該指針指向一個(gè)函數(shù),函數(shù)具有兩個(gè) int * 類(lèi)型的參數(shù),且返回值類(lèi)型為 int。


尾言
作業(yè):解析:void(*pFunc)(int(*)(int,int),int,int);
指針的動(dòng)態(tài)內(nèi)存申請(qǐng)與結(jié)構(gòu)體指針在后續(xù)篇章中給大家做解析。

