- 相關(guān)推薦
什么是數(shù)組名
實(shí)際上數(shù)組名永遠(yuǎn)都不會(huì)是指針! 指針是C語言具有低級(jí)語言特征的最直接的證據(jù)。在匯編語言里面,指針的概念隨處可見。比如SP,SP寄存器又叫堆棧指針,它的值是地址,由于SP保存的是地址,并且SP的值是不斷變化的,因此可以看作一個(gè)變量,而且是一個(gè)地址變量。地址也是C語言指針的值,C語言的指針跟SP這樣的寄存器雖然不完全一樣,但原理卻是相通的。C語言的指針也是一種地址變量,C89明確規(guī)定,指針是一個(gè)保存對(duì)象地址的變量。這里要注意的是,指針跟地址概念的不同,指針是一種地址變量,通常也叫指針變量,統(tǒng)稱指針。而地址則是地址變量的值。
看到這里,也許你會(huì)覺得,這么簡單的東西還用你來說嗎?的確,對(duì)于p與&p來說,99%的人都能在0.1秒內(nèi)脫口而出誰是指針,誰是地址,但是,又有多少人在使用指針的過程中能夠始終如一毫不動(dòng)搖地遵循這兩個(gè)概念呢?不少人使用指針的時(shí)候就會(huì)自覺或不自覺地把指針和地址兩個(gè)概念混淆得一塌糊涂了,數(shù)組名的濫用就是一個(gè)活生生的例子。這一點(diǎn)甚至連一些經(jīng)典著作也沒能避免。
不過也不能全怪你自己,筆者認(rèn)為某些國內(nèi)教材應(yīng)該承擔(dān)最大的責(zé)任。這些教材一開始就沒有給讀者好好地分清指針與地址的區(qū)別,相反還在講述的過程中有意無意地混用這兩個(gè)概念。更有甚者,甚至在書中明言指針就是地址!說這話的家伙最應(yīng)該在C語言這個(gè)地圖上抹掉,呵呵。兩個(gè)月前我在購書中心隨手翻開了某個(gè)作者主編的一本被冠以國家“十五”規(guī)劃重點(diǎn)研究項(xiàng)目的書,書里就是這么寫的。當(dāng)時(shí)筆者就感慨:不知道又要有多少人的思想被這家伙“性暴力”了。
實(shí)際上,地址這個(gè)東西,本來就是一種基本數(shù)據(jù)類型,本應(yīng)該在介紹整數(shù)、浮點(diǎn)、字符等基本類型的時(shí)候把地址顯式地放在一起討論,這樣在后面介紹指針與數(shù)組的時(shí)候就能避免許多誤解?上Р簧俳滩幕蛘吒緵]有談及,或者就算提起這個(gè)類型也用了指針類型這個(gè)字眼。這就錯(cuò)了,指針不是類型,真正的類型是地址,指針只是存儲(chǔ)地址這種數(shù)據(jù)類型的變量!打個(gè)比方,對(duì)于
int i=10;
10是整數(shù),而i是存儲(chǔ)整數(shù)的變量,指針就好比這個(gè)i,地址就好比那個(gè)10。指針能夠進(jìn)行加減法,原因并不是因?yàn)樗侵羔,加減法則不是屬于指針這種變量的,而是地址這種數(shù)據(jù)類型的本能,正是因?yàn)榈刂肪哂屑訙p的能力,所以才使指針作為存放地址的變量能夠進(jìn)行加減運(yùn)算。這跟整數(shù)變量因?yàn)檎麛?shù)能夠進(jìn)行加減乘除因而它也能進(jìn)行加減乘除一個(gè)道理。
那么數(shù)組名又應(yīng)該如何理解呢?用來存放數(shù)組的區(qū)域是一塊在棧中靜態(tài)分配的內(nèi)存(非static),而數(shù)組名是這塊內(nèi)存的代表,它被定義為這塊內(nèi)存的首地址。這就說明了數(shù)組名是一個(gè)地址,而且,還是一個(gè)不可修改的常量,完整地說,就是一個(gè)地址常量。數(shù)組名跟枚舉常量類似,都屬于符號(hào)常量。數(shù)組名這個(gè)符號(hào),就代表了那塊內(nèi)存的首地址。注意了!不是數(shù)組名這個(gè)符號(hào)的值是那塊內(nèi)存的首地址,而是數(shù)組名這個(gè)符號(hào)本身就代表了首地址這個(gè)地址值,它就是這個(gè)地址,這就是數(shù)組名屬于符號(hào)常量的意義所在。由于數(shù)組名是一種符號(hào)常量,因此它是一個(gè)右值,而指針,作為變量,卻是一個(gè)左值,一個(gè)右值永遠(yuǎn)都不會(huì)是左值,那么,數(shù)組名永遠(yuǎn)都不會(huì)是指針!不管什么話,只要說數(shù)組名是一個(gè)指針的,都是錯(cuò)誤的!就象把剛才int i=10例子中的10說成是整數(shù)變量一樣,在最基本的立足點(diǎn)上就已經(jīng)完錯(cuò)了。
總之要牢牢記住,數(shù)組名是一個(gè)地址,一個(gè)符號(hào)地址常量,不是一個(gè)變量,更不是一個(gè)作為變量的指針!
在數(shù)組名并非指針這個(gè)問題上,通常會(huì)產(chǎn)生兩種疑問:
1。作為形參的數(shù)組,不是會(huì)被轉(zhuǎn)換為指針嗎?
2。如果形參是一個(gè)指針,數(shù)組名可以作為實(shí)參傳遞給那個(gè)指針,難道不是說明了數(shù)組名是一個(gè)指針嗎?
首先,C語言之所以把作為形參的數(shù)組看作指針,并非因?yàn)閿?shù)組名可以轉(zhuǎn)換為指針,而是因?yàn)楫?dāng)初ANSI委員會(huì)制定標(biāo)準(zhǔn)的時(shí)候,從C程序的執(zhí)行效率出發(fā),不主張參數(shù)傳遞時(shí)復(fù)制整個(gè)數(shù)組,而是傳遞數(shù)組的首地址,由被調(diào)函數(shù)根據(jù)這個(gè)首地址處理數(shù)組中的內(nèi)容。那么誰能承擔(dān)這種“轉(zhuǎn)換”呢?這個(gè)主體必須具有地址數(shù)據(jù)類型,同時(shí)應(yīng)該是一個(gè)變量,滿足這兩個(gè)條件的,非指針莫屬了。要注意的是,這種“轉(zhuǎn)換”只是一種邏輯看法上的轉(zhuǎn)換,實(shí)際當(dāng)中并沒有發(fā)生這個(gè)過程,沒有任何數(shù)組實(shí)體被轉(zhuǎn)換為指針實(shí)體。另一方面,大家不要被“轉(zhuǎn)換”這個(gè)字眼給蒙蔽了,轉(zhuǎn)換并不意味著相同,實(shí)際上,正是因?yàn)椴幌嗤艜?huì)有轉(zhuǎn)換,相同的話還轉(zhuǎn)來干嗎?這好比現(xiàn)在社會(huì)上有不少人“變性”,一個(gè)男人可以“轉(zhuǎn)換”為一個(gè)女人,那是不是應(yīng)該認(rèn)為男人跟女人是相同的?這不是笑話么。
第二,函數(shù)參數(shù)傳遞的過程,本質(zhì)上是一種賦值過程。C89對(duì)函數(shù)調(diào)用是這樣規(guī)定的:函數(shù)調(diào)用由一個(gè)后綴表達(dá)式(稱為函數(shù)標(biāo)志符,function designator)后跟由圓括號(hào)括起來的賦值表達(dá)式列表組成,在調(diào)用函數(shù)之前,函數(shù)的每個(gè)實(shí)際參數(shù)將被復(fù)制,所有的實(shí)際參數(shù)嚴(yán)格地按值傳遞。因此,形參實(shí)際上所期望得到的東西,并不是實(shí)參本身,而是實(shí)參的值或者實(shí)參所代表的值!舉個(gè)例來說,對(duì)于一個(gè)函數(shù)聲明:
void fun(int i);
我們可以用一個(gè)整數(shù)變量int n作實(shí)參來調(diào)用fun,就是fun(n);當(dāng)然,也正如大家所熟悉的那樣,可以用一個(gè)整數(shù)常量例如10來做實(shí)參,就是fun(10);那么,按照第二個(gè)疑問的看法,由于形參是一個(gè)整數(shù)變量,而10可以作為實(shí)參傳遞給i,豈不就說明10是一個(gè)整數(shù)變量嗎?這顯然是謬誤。實(shí)際上,對(duì)于形參i來說,用來聲明i的類型說明符int,所起的作用是用來說明需要傳遞給i一個(gè)整數(shù),并非要求實(shí)參也是一個(gè)整數(shù)變量,i真正所期望的,只是一個(gè)整數(shù),僅此而已,至于實(shí)參是什么,跟i沒有任何關(guān)系,它才不管呢,只要能正確給i傳遞一個(gè)整數(shù)就OK了。當(dāng)形參是指針的時(shí)候,所發(fā)生的事情跟這個(gè)是相同的。指針形參并沒有要求實(shí)參也是一個(gè)指針,它需要的是一個(gè)地址,誰能給予它一個(gè)地址?顯然指針、地址常量和符號(hào)地址常量都能滿足這個(gè)要求,而數(shù)組名作為符號(hào)地址常量正是指針形參所需要的地址,這個(gè)過程就跟把一個(gè)整數(shù)賦值給一個(gè)整數(shù)變量一樣簡單!
首先我們要了解內(nèi)存的分配方式。一般來說,內(nèi)存的分配方式有三種:
1.從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static變量。
2.在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
3.從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用free或釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。
以上三種分配方式,我們要注意內(nèi)存生命期的問題:
1.靜態(tài)分配的區(qū)域的生命期是整個(gè)軟件運(yùn)行期,就是說從軟件運(yùn)行開始到軟件終止退出。只有軟件終止運(yùn)行后,這塊內(nèi)存才會(huì)被系統(tǒng)回收
2.在棧中分配的空間的生命期與這個(gè)變量所在的函數(shù)和類相關(guān)。如果是函數(shù)中定義的局部變量,那么它的生命期就是函數(shù)被調(diào)用時(shí),如果函數(shù)運(yùn)行結(jié)束,那么這塊內(nèi)存就會(huì)被回收。如果是類中的成員變量,則它的生命期與類實(shí)例的生命期相同。
3.在堆上分配的內(nèi)存,生命期是從調(diào)用new或者malloc開始,到調(diào)用或者free結(jié)束。如果不掉用或者free。則這塊空間必須到軟件運(yùn)行結(jié)束后才能被系統(tǒng)回收。
下面我們?cè)倏纯,在使用?nèi)存的過程中,我們經(jīng)常發(fā)生一些什么樣的錯(cuò)誤。以及我們應(yīng)該采取哪些對(duì)策。
發(fā)生內(nèi)存錯(cuò)誤是件非常麻煩的事情。編譯器不能自動(dòng)發(fā)現(xiàn)這些錯(cuò)誤,通常是在程序運(yùn)行時(shí)才能捕捉到。而這些錯(cuò)誤大多沒有明顯的癥狀,時(shí)隱時(shí)現(xiàn),增加了改錯(cuò)的難度。有時(shí)用戶怒氣沖沖地把你找來,程序卻沒有發(fā)生任何問題,你一走,錯(cuò)誤又發(fā)作了。
常見的內(nèi)存錯(cuò)誤及其對(duì)策如下:
1 內(nèi)存分配未成功,卻使用了它。
常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進(jìn)行檢查。如果是用malloc或new來申請(qǐng)內(nèi)存,應(yīng)該用if(p==NULL) 或if(p!=NULL)進(jìn)行防錯(cuò)處理。
2 內(nèi)存分配雖然成功,但是尚未初始化就引用它。
內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標(biāo)準(zhǔn),盡管有些時(shí)候?yàn)榱阒担覀儗幙尚牌錈o不可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
3 內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界。
例如在使用數(shù)組時(shí)經(jīng)常發(fā)生下標(biāo)”多1″或者”少1″的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯(cuò),導(dǎo)致數(shù)組操作越界。
4 忘記了釋放內(nèi)存,造成內(nèi)存泄露。
含有這種錯(cuò)誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開始時(shí)系統(tǒng)的內(nèi)存充足,你看不到錯(cuò)誤。終有一次程序突然死掉,系統(tǒng)出現(xiàn)提示:內(nèi)存耗盡。
動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),程序中malloc與free的使用次數(shù)一定要相同,否則肯定有錯(cuò)誤(new/同理)。
5 釋放了內(nèi)存卻繼續(xù)使用它。
有三種情況:
(1)程序中的對(duì)象調(diào)用關(guān)系過于復(fù)雜,實(shí)在難以搞清楚某個(gè)對(duì)象究竟是否已經(jīng)釋放了內(nèi)存,此時(shí)應(yīng)該重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),從根本上解決對(duì)象管理的混亂局面。
(2)函數(shù)的return語句寫錯(cuò)了,注意不要返回指向”棧內(nèi)存”的”指針”或者”引用”,因?yàn)樵搩?nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)銷毀。
條件編譯命令最常見的形式為:
#ifdef 標(biāo)識(shí)符
程序段1
#else
程序段2
#endif
它的作用是:當(dāng)標(biāo)識(shí)符已經(jīng)被定義過(一般是用#define命令定義),則對(duì)程序段1進(jìn)行編譯,否則編譯程序段2。
其中#else部分也可以沒有,即:
#ifdef
程序段1
#endif
這里的”程序段”可以是語句組,也可以是命令行。這種條件編譯可以提高C源程序的通用性。如果一個(gè)C源程序在不同計(jì)算機(jī)系統(tǒng)上系統(tǒng)上運(yùn)行,而不同的計(jì)算機(jī)又有一定的差異。例如,我們有一個(gè)數(shù)據(jù)類型,在Windows平臺(tái)中,應(yīng)該使用long類型表示,而在其他平臺(tái)應(yīng)該使用float表示,這樣往往需要對(duì)源程序作必要的修改,這就降低了程序的通用性?梢杂靡韵碌臈l件編譯:
#ifdef WINDOWS
#define MYTYPE long
#else
#define MYTYPE float
#endif
如果在Windows上編譯程序,則可以在程序的開始加上
#define WINDOWS
這樣則編譯下面的命令行:
#define MYTYPE long
如果在這組條件編譯命令之前曾出現(xiàn)以下命令行:
#define WINDOWS 0
則預(yù)編譯后程序中的MYTYPE都用float代替。這樣,源程序可以不必作任何修改就可以用于不同類型的計(jì)算機(jī)系統(tǒng)。當(dāng)然以上介紹的只是一種簡單的情況,可以根據(jù)此思路設(shè)計(jì)出其它的條件編譯。
例如,在調(diào)試程序時(shí),常常希望輸出一些所需的信息,而在調(diào)試完成后不再輸出這些信息。可以在源程序中插入以下的條件編譯段:
#ifdef DEBUG
print (“device_open(%p) ”, file);
#endif
如果在它的前面有以下命令行:
#define DEBUG
則在程序運(yùn)行時(shí)輸出file指針的值,以便調(diào)試分析。調(diào)試完成后只需將這個(gè)define命令行刪除即可。有人可能覺得不用條件編譯也可達(dá)此目的,即在調(diào)試時(shí)加一批printf語句,調(diào)試后一一將printf語句刪除去。的確,這是可以的。但是,當(dāng)調(diào)試時(shí)加的printf語句比較多時(shí),修改的工作量是很大的。用條件編譯,則不必一一刪改printf語句,只需刪除前面的一條”#define DEBUG”命令即可,這時(shí)所有的用DEBUG作標(biāo)識(shí)符的條件編譯段都使其中的printf語句不起作用,即起統(tǒng)一控制的作用,如同一個(gè)”開關(guān)”一樣。
有時(shí)也采用下面的形式:
#ifndef 標(biāo)識(shí)符
程序段1
#else
程序段2
#endif
只是第一行與第一種形式不同:將”ifdef”改為”ifndef”。它的作用是:若標(biāo)識(shí)符未被定義則編譯程序段1,否則編譯程序段2。這種形式與第一種形式的作用相反。
以上兩種形式用法差不多,根據(jù)需要任選一種,視方便而定。
還有一種形式,就是#if后面的是一個(gè)表達(dá)式,而不是一個(gè)簡單的標(biāo)識(shí)符:
#if 表達(dá)式
程序段1
#else
程序段2
#endif
它的作用是:當(dāng)指定的表達(dá)式值為真(非零)時(shí)就編譯程序段1,否則編譯程序段2?梢允孪冉o定一定條件,使程序在不同的條件下執(zhí)行不同的功能。
例如:輸入一行字母字符,根據(jù)需要設(shè)置條件編譯,使之能將字母全改為大寫輸出,或全改為小寫字母輸出。
#define LETTER 1
main()
{
char str[20]=”C Language”,c;
int i=0;
while((c=str[i])!=’′){
i++;
#if LETTER
if(c>=’a'&&c<=’z') c=c-32;
#else
if(c>=’A'&&c<=’Z') c=c+32;
#endif
printf(“%c”,c);
}
}
運(yùn)行結(jié)果為:C LANGUAGE
現(xiàn)在先定義LETTER為1,這樣在預(yù)處理?xiàng)l件編譯命令時(shí),由于LETTER為真(非零),則對(duì)第一個(gè)if語句進(jìn)行編譯,運(yùn)行時(shí)使小寫字母變大寫。如果將程序第一行改為:
#define LETTER 0
則在預(yù)處理時(shí),對(duì)第二個(gè)if語句進(jìn)行編譯處理,使大寫字母變成小寫字母(大寫字母與相應(yīng)的小寫字母的ASCII代碼差32)。此時(shí)運(yùn)行情況為:
c language
有人會(huì)問:不用條件編譯命令而直接用if語句也能達(dá)到要求,用條件編譯命令有什么好處呢?的確,此問題完全可以不用條件編譯處理,但那樣做目標(biāo)程序長(因?yàn)樗姓Z句都編譯),而采用條件編譯,可以減少被編譯的語句,從而減少目標(biāo)的長度。當(dāng)條件編譯段比較多時(shí),目標(biāo)程序長度可以大大減少。
【什么是數(shù)組名】相關(guān)文章:
風(fēng)什么雨什么成語01-16
什么吁什么嘆的成語05-23
一什么就什么寫句子10-22
什么性格開什么店03-04
檔案是什么,有什么作用07-16
半什么半什么的成語03-05
山什么路什么的成語06-05
三什么六什么的成語02-10
虎什么蛇什么的成語11-17
什么天什么地的成語大全12-02