C51學(xué)習(xí)筆記,數(shù)組和指針的程序設(shè)計(jì)
終于說到了指針。指針是C語言的精華部分,如果沒有指針,c語言對底層的許多操作將無法完成。也是因?yàn)橹羔樀拇嬖?,使得c語言看起來并不那么高級(jí),因?yàn)橹羔槻僮鞯膶ο蟮氖莾?nèi)存地址,想要熟練地進(jìn)行指針操作,必須考慮到內(nèi)存等偏硬件方面的東西。當(dāng)然,也不需要了解過多。但是,數(shù)據(jù)結(jié)構(gòu)這一關(guān)還是要過的。我對數(shù)據(jù)結(jié)構(gòu)方面了解尚淺,就不多說了。數(shù)組與指針的關(guān)系如此復(fù)雜,讓我不得不照著書來寫這一篇筆記了。
一、數(shù)組不等于指針
C語言中,對數(shù)組的操作,是仿照指針的模式進(jìn)行的。但是需要記住一點(diǎn),數(shù)組不等于指針。對于一維數(shù)組a[],指向數(shù)組的指針p=a來說,他們之間最大的區(qū)別在于,數(shù)組方式使用數(shù)組名a(同時(shí)也是數(shù)組的首地址)對數(shù)組進(jìn)行直接的訪問和操作,而指針方式使用指針名p對數(shù)組進(jìn)行的是間接的訪問和操作。在多數(shù)情況下,他們操作結(jié)果是相同的,但是也有例外。
如果我們在文件外定義了一個(gè)指針p,int *p=a(a是一個(gè)整型數(shù)組)。在文件內(nèi)用到p時(shí),需要用extern聲明一下,表明p是個(gè)外部變量,在外部定義好了。如果我們聲明為指針 extern int * p,然后去使用,肯定是沒有問題的。但是如果我們聲明為數(shù)組extern int p[] ,問題就出現(xiàn)了,編譯系統(tǒng)處理的時(shí)候會(huì)使用數(shù)組方式對指針進(jìn)行操作。也就是說,當(dāng)我們想得到*p,也就是*a,a[0]的值的時(shí)候,因?yàn)橄到y(tǒng)把p當(dāng)作了數(shù)組首地址,所以*p并不能得到a[0],得到的是p所存放的地址值,也就是a[0]的地址。這種情況下,一定要使extern聲明與定義相匹配。
數(shù)組的直接訪問數(shù)據(jù)模式與指針的間接訪問數(shù)據(jù)模式,是兩者之間最根本的不同。數(shù)組不等于指針。數(shù)組通常用于存儲(chǔ)固定數(shù)目且數(shù)據(jù)類型相同的元素,數(shù)組所占用的內(nèi)存是隱式分配和刪除的,數(shù)組中保存數(shù)據(jù),并且數(shù)組中的每個(gè)元素都有唯一且明確的變量名來標(biāo)識(shí)數(shù)據(jù),使用數(shù)組可以直接訪問數(shù)據(jù)也就是說a[i]只是簡單地以a+i為地址取得數(shù)據(jù)。而指針通常用于動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),指針變量保存的是數(shù)據(jù)的地址(其中包括變量的地址,也包括不匿名數(shù)據(jù)),使用指針訪問數(shù)據(jù)采用的是間接訪問模式,即首先取得指針的內(nèi)容,把它作為地址,然后從這個(gè)地址提取數(shù)據(jù)。如果指針有下標(biāo),p[i]就是先去的指針p的內(nèi)容,然后把指針p的內(nèi)容加上i作為地址,從中提取數(shù)據(jù)。指針可以指向匿名數(shù)據(jù),所以要學(xué)會(huì)用指針操作匿名內(nèi)存,c語言中與內(nèi)存空間相關(guān)的函數(shù)為malLOC(), free()。
在定義指針時(shí),編譯器并不為指針?biāo)傅膶ο蠓峙鋬?nèi)存空間,只是分配指針本身的空間,除非在定義字符指針(必須是指向字符型的)的同時(shí)用字符串常量進(jìn)行初始化。其實(shí)就算是這種情況,也可當(dāng)作是編譯器為此字符串常量分配了內(nèi)存空間后,在為字符指針本身分配了空間,并使字符指針指向字符串常量的首地址。在ANSI C中,初始化指針?biāo)鶆?chuàng)建的字符串常量被定義為只讀(?這一點(diǎn),我用turbo C試了試,好像可以修改)。數(shù)組也可以用字符串常量進(jìn)行初始化,但與指針襄樊,由字符串常量初始化的數(shù)組是可以修改的,原因很簡單,由字符串常量初始化的數(shù)組本來就是一個(gè)字符數(shù)組,每個(gè)字符都有確定的變量名與之對應(yīng),所以當(dāng)然可以修改單個(gè)字符了。而初始化指針?biāo)鶆?chuàng)建的字符串常量,其實(shí)是匿名的數(shù)據(jù),如果你把指向字符串常量的字符指針賦予了其他地址,這個(gè)字符串常量顯然再也找不到了。
由此可見,數(shù)組和指針在編譯器處理時(shí)是不同的,在運(yùn)行時(shí)的表示形式也是不一樣的,并且可能產(chǎn)生不同的代碼。對編譯器而言,一個(gè)數(shù)組就是一個(gè)地址,一個(gè)指針就是一個(gè)地址的地址。所以,在外部數(shù)組的聲明時(shí),在數(shù)組的定義(因?yàn)閿?shù)組的定義必然要分配內(nèi)存空間)時(shí),不能用指針來替代數(shù)組。
還有,在下列情況下,對數(shù)組的引用不能用指向該數(shù)組第一個(gè)元素的指針來替代:
1. 數(shù)組名作為sizeof()的操作數(shù),因?yàn)榇藭r(shí)需要的是整個(gè)數(shù)組的大小,而不是指針?biāo)赶虻牡谝粋€(gè)元素的大小;
2. 使用&操作符取數(shù)組的地址。&操作符的主要用途是實(shí)現(xiàn)傳址調(diào)用。指針本身就是地址,所以對指針使用&意義不大。
3. 數(shù)組是一個(gè)字符串常量初始值。這一點(diǎn)上面已經(jīng)提到,不多說了。字符串常量初始化數(shù)組必須一氣呵成,不能分成兩了語句。也就是說,字符串常量只能對數(shù)組進(jìn)行聲明初始化,不能用字符串常量對數(shù)組賦值。這點(diǎn)是因?yàn)镃語言中只能夠在數(shù)組聲明時(shí)對它進(jìn)行初始化(這點(diǎn)沒啥原因,也許是因?yàn)閿?shù)組名是不可修改的左值吧),不能對數(shù)組名賦值,只能對數(shù)組元素逐個(gè)賦值。
4. 數(shù)組名是不可修改的左值,它的值是數(shù)組第一個(gè)元素的地址,不可以改變。
二、什么時(shí)候數(shù)組與指針相同
大多數(shù)的時(shí)候,數(shù)組和指針可以互換。
1. 除了個(gè)別情況,表達(dá)式中的數(shù)組名(數(shù)組在使用中,而不是聲明中)就是指針
假如我們聲明:int a[10], *p, i=2;
就可以通過以下方法來訪問a[i] :
a[i];
*(a+i);
p=a ; p[i] ;
p=a ; *(p+i) ;
p=a+i ; *p ;
實(shí)際上,對數(shù)組的引用如a[i]在編譯時(shí)總是被編譯器改寫成*(a+i)的形式。所以如加法一樣,取下標(biāo)操作符的操作數(shù)是可以交換的(a[i] 與i[a] 都是正確的,等價(jià)的)。
編譯器自動(dòng)把下標(biāo)值得步長調(diào)整到數(shù)組元素的大小。對起始地址執(zhí)行加法操作之前,編譯器會(huì)負(fù)責(zé)計(jì)算每次增加的步長。這就是為什么指針總是有類型限制,每個(gè)指針只能指向一種類型的原因所在。
2. 數(shù)組下標(biāo)總是與指針的偏移量相同
數(shù)組下標(biāo)總是與指針的偏移量相同,所以程序員完全可以使用指針來訪問數(shù)組,從而繞過下標(biāo)操作符。在這種情況下,對數(shù)組下標(biāo)范圍檢查并不能檢測所有對數(shù)組的訪問情況。因此,C語言并不進(jìn)行下標(biāo)范圍檢測。但是我們編寫程序的時(shí)候,可要小心,不要越界。
在處理以為數(shù)組時(shí),指針并不比數(shù)組更快。C語言吧數(shù)組下標(biāo)改寫成指針偏移量的根本原因是指針和偏移量是底層硬件所使用的基本模型。
3. 作為函數(shù)參數(shù)的數(shù)組名等同于指針
吧作為形參的數(shù)組和指針等同起來是出于效率原因的考慮。在函數(shù)參數(shù)的聲明中,數(shù)組名被編譯器當(dāng)中指向該數(shù)據(jù)第一個(gè)元素的指針。編譯器只向函數(shù)傳遞數(shù)組的地址,而不是整個(gè)數(shù)字的拷貝。隱性轉(zhuǎn)換意味著下面三種函數(shù)定義形式是完全相同的:
fun(int *p){...}
fun(int p[]) {...}
fun(int p[10]) {...}
在函數(shù)的聲明和調(diào)用上,使用數(shù)組或者指向數(shù)組第一個(gè)元素的指針都是合法的。
注意,這里第一個(gè)元素的含義,這第一個(gè)元素可以是數(shù)值變量、字符變量、數(shù)組、結(jié)構(gòu)體、指針。
三、總結(jié)
數(shù)組不等于指針,但是數(shù)組可以用指針等效;指針始終是指針,指針絕對不可以改寫為數(shù)組。你可以使用下標(biāo)方式訪問指針,一般是指針作為函數(shù)參數(shù)時(shí),一般是指針指向的是數(shù)組元素時(shí)。
在外部聲明和定義時(shí),數(shù)組不能拿指針來等效,除了數(shù)組作為函數(shù)參數(shù)之外,定義和聲明必須匹配。
在使用時(shí),數(shù)組基本可以與指針互換。a[i]總是被編譯器解釋為*(a+i)。
在作為函數(shù)參數(shù)時(shí),數(shù)組可以與指針互換。編譯器總是把函數(shù)參數(shù)的數(shù)組修改為指向數(shù)組第一個(gè)元素的指針。在函數(shù)內(nèi)部獲得的事實(shí)上都是一個(gè)指針。
c語言實(shí)際上沒有多維數(shù)組。只有數(shù)組元素是數(shù)組的數(shù)組。多維數(shù)組作為函數(shù)參數(shù),傳遞的指針類型是指向數(shù)組的指針。