一說(shuō)到指針和數(shù)組,很多人都會(huì)潛意識(shí)的認(rèn)為它們是等同的鳄炉,但事實(shí)上指針和數(shù)組只在某些前提下等同。本篇將詳細(xì)介紹這些知識(shí)。
4.1 指針等同于數(shù)組的情況
ANSI C中關(guān)于什么時(shí)候數(shù)組和指針時(shí)相同的障癌,定義了如下規(guī)則:
規(guī)則1.表達(dá)式中的數(shù)組名被編譯器當(dāng)作一個(gè)指向該數(shù)組第一個(gè)元素的指針(具體見ANSI C標(biāo)準(zhǔn)第6.2.2.1節(jié))。
規(guī)則2.下標(biāo)總是與指針的偏移量相同(具體見ANSI C標(biāo)準(zhǔn)第6.3.2.1節(jié))辩尊。
規(guī)則3.在函數(shù)參數(shù)的聲明中涛浙,數(shù)組名被編譯器當(dāng)作指向該數(shù)組第一個(gè)元素的指針(具體見ANSI C標(biāo)準(zhǔn)第6.7.2.1節(jié))。
根據(jù)規(guī)則1和規(guī)則2摄欲,我們可以知道轿亮,對(duì)數(shù)組下標(biāo)的引用總是可以寫成“一個(gè)指向數(shù)組的起始地址的指針加上偏移量”。
例如胸墙,我們聲稱:int a[10]; int *p,i=2;則我注,我們可以通過(guò)以下的方式來(lái)訪問a[i]:
int a[10]; int *p,i=2;
p=a; p[i];
p=a; *(p + 1);
p=a+i; *p;
根據(jù)規(guī)則1、2迟隅,對(duì)數(shù)組的引用如a[i]在編譯時(shí)總是被編譯器改寫成(a+i)但骨。不管你是人為的顯示用(a+i)表達(dá)式還是a[i],最終在編譯器那都是*(a+i)玻淑。這也是人們常說(shuō)指針和數(shù)組相同的原因之一嗽冒。因?yàn)榇藭r(shí),不管是真的指針還是數(shù)組补履,都可以用指針\數(shù)組表示方法來(lái)取值添坊。
根據(jù)規(guī)則3,我們可以知道箫锤,在函數(shù)的形參中贬蛙,不管你是寫數(shù)組還是指針,最后編譯器都會(huì)按照指針的方式進(jìn)行解釋谚攒,也就是說(shuō)以下幾種方式是等價(jià)的:
int Func(int *pNow);
int Func(int Now[]);
int Func(int Now[200]);
因?yàn)檫@幾種聲明方式的最終結(jié)果都是int Func(int*)阳准;
C語(yǔ)言之所以把數(shù)組形參解釋成指針的主要原因是為了提高參數(shù)傳遞的效率。因?yàn)榘褦?shù)組名解釋成指向數(shù)組第一個(gè)元素的指針后馏臭,在參數(shù)傳遞的時(shí)候只需要把第一個(gè)元素的地址賦值給形參就行了野蝇,而不必將整個(gè)數(shù)組的值都賦值給形參。和這個(gè)道理類似,函數(shù)的返回值也不能是數(shù)組绕沈,而應(yīng)用指針代替锐想。但不同的是,在語(yǔ)法上函數(shù)的參數(shù)可以是數(shù)組(實(shí)質(zhì)是指針)而函數(shù)的返回值不能是數(shù)組乍狐。
由于C語(yǔ)言并不提供對(duì)數(shù)組邊界的檢查赠摇,而且這種檢查即費(fèi)時(shí)也沒有必要。所以浅蚪,上述第二中寫法和第三種寫法是相同的藕帜。只有在定義數(shù)組時(shí),數(shù)組的長(zhǎng)度才是必要的惜傲,其他的情況下有沒有數(shù)組的長(zhǎng)度都無(wú)關(guān)緊要洽故。
注:由于形參中的數(shù)組總是被轉(zhuǎn)換為指向數(shù)組第一個(gè)元素的指針,所以用sizeof取的數(shù)組的大小始終是4操漠。因?yàn)橹羔樀拇笮【褪?收津。
4.2 指針和數(shù)組的區(qū)別
如果指針和數(shù)組沒有區(qū)別,那么就不需要這兩種基本數(shù)據(jù)類型了浊伙。由于撞秋,指針和數(shù)組是常見的數(shù)據(jù)類型,我只簡(jiǎn)要的羅列下它們的區(qū)別嚣鄙。
區(qū)別1:指針只占4個(gè)字節(jié)吻贿,而數(shù)組將根據(jù)其長(zhǎng)度占據(jù)一定的連續(xù)的空間。
區(qū)別2:雖然在表達(dá)式中數(shù)組名解釋為指向數(shù)組中第一個(gè)元素的指針哑子,但是用sizeof(數(shù)組名)得到的是整個(gè)數(shù)組的大小而不是4舅列。
區(qū)別3:指針是可以修改的左值,而數(shù)組名是不可以修改的左值卧蜓。
例:int a[10],b[10]; a=b;(非法)
注:左值是指編譯時(shí)可知的用于存儲(chǔ)結(jié)果的地方帐要。右值是指直到運(yùn)行時(shí)才知道的某個(gè)地址的內(nèi)容。
區(qū)別4:定義一個(gè)字符指針并初始化一個(gè)字符串和定義一個(gè)數(shù)組并初始化一個(gè)字符串是不一樣的弥奸。
例:char a[]=”abcdefg”; 和char *a=”abcdefg”;不一樣榨惠。
前者是局部/全局(取決于定義的位置),后者存儲(chǔ)于常量區(qū)盛霎,不能再修改赠橙。兩者都以’\0’結(jié)束。
注:只有字符串常量才能在定義時(shí)給char *p賦值愤炸,其他類型的不行期揪,例如int *p = 1; 錯(cuò)誤
區(qū)別5:數(shù)組和指針訪問元素的過(guò)程不一樣。
由于數(shù)組的名字就是數(shù)組第一個(gè)元素的地址规个,而指針的名字是指針的地址凤薛,指針地址里的內(nèi)容是數(shù)組首元素的地址姓建,所以再用指針訪問時(shí)會(huì)多一次間接尋址。
例:char a []="abcdefg"; char *p = a;則:
a[i]的訪問方式是:先取i的值枉侧,再加上a引瀑,再在此地址中取值。
p[i]的訪問方式是:先通過(guò)p的值取出a的值榨馁,再取i的值,再加上a帜矾,再在此地址中取值翼虫。
當(dāng)我們把某個(gè)變量聲明為數(shù)組時(shí),它就會(huì)按照數(shù)組的方式存儲(chǔ)屡萤,當(dāng)我們?cè)诒磉_(dá)式中用到此數(shù)組名時(shí)珍剑,雖然它總是被解釋為*(a+i)的形式,但此時(shí)編譯知道a是數(shù)組名死陆,它的值就是數(shù)組首元素的地址了招拙,此時(shí)不需要間接尋址的。而對(duì)于指針措译,則會(huì)多一次間接尋址别凤。
注:通過(guò)區(qū)別5,我們知道指針和數(shù)組在取值時(shí)的過(guò)程不一樣领虹。那么我們就不要指望在定義和使用時(shí)分別用指針和數(shù)組了规哪。(此處不是只形式,而是指實(shí)質(zhì))比如塌衰,在一個(gè)文件中定義int a[20];而在另一個(gè)文件中聲明extern int *a;不要指望后面這個(gè)a指針就是前面的數(shù)組诉稍,因?yàn)榇藭r(shí)在前面一個(gè)文件中對(duì)a的引用時(shí)數(shù)組方式,而在后一個(gè)文件中對(duì)a的引用是指針方式最疆,這兩種方式不可能相等杯巨。