?? 第2章 變量和數(shù)據(jù)存儲(chǔ).txt
字號(hào):
C語(yǔ)言編程常見(jiàn)問(wèn)題解答
發(fā)表日期:2003年9月21日 作者:C語(yǔ)言之家整理 已經(jīng)有7602位讀者讀過(guò)此文
第2章 變量和數(shù)據(jù)存儲(chǔ)
C語(yǔ)言的強(qiáng)大功能之一是可以靈活地定義數(shù)據(jù)的存儲(chǔ)方式。C語(yǔ)言從兩個(gè)方面控制變量的性質(zhì):作用域(scope)和生存期(lifetime)。作用域是指可以存取變量的代碼范圍,生存期是指可以存取變量的時(shí)間范圍。
作用域有三種:
1. extern(外部的) 這是在函數(shù)外部定義的變量的缺省存儲(chǔ)方式。extern變量的作用域是整個(gè)程序。
2.static(靜態(tài)的) 在函數(shù)外部說(shuō)明為static的變量的作用域?yàn)閺亩x點(diǎn)到該文件尾部;在函數(shù)內(nèi)部說(shuō)明為static的變量的作用域?yàn)閺亩x點(diǎn)到該局部程序塊尾部。
3.a(chǎn)uto(自動(dòng)的) 這是在函數(shù)內(nèi)部說(shuō)明的變量的缺省存儲(chǔ)方式。auto變量的作用域?yàn)閺亩x點(diǎn)到該局部程序塊尾部。
變量的生存期也有三種,但它們不象作用域那樣有預(yù)定義的關(guān)鍵字名稱(chēng)。第一種是extern和static變量的生存期,它從main()函數(shù)被調(diào)用之前開(kāi)始,到程序退出時(shí)為止。第二種是函數(shù)參數(shù)和auto變量的生存期,它從函數(shù)調(diào)用時(shí)開(kāi)始,到函數(shù)返回時(shí)為止。第三種是動(dòng)態(tài)分配的數(shù)據(jù)的生存期,它從程序調(diào)用malloc()或calloc()為數(shù)據(jù)分配存儲(chǔ)空間時(shí)開(kāi)始,到程序調(diào)用free()或程序退出時(shí)為止。
2.1 變量存儲(chǔ)在內(nèi)存(memory)中的什么地方?
變量可以存儲(chǔ)在內(nèi)存中的不同地方,這依賴(lài)于它們的生存期。在函數(shù)外部定義的變量(全局變量或靜態(tài)外部變量)和在函數(shù)內(nèi)部定義的static變量,其生存期就是程序運(yùn)行的全過(guò)程,這些變量被存儲(chǔ)在數(shù)據(jù)段(datasegment)中。數(shù)據(jù)段是在內(nèi)存中為這些變量留出的一段大小固定的空間,它分為兩部分,一部分用來(lái)存放初始化變量,另一部分用來(lái)存放未初始化變量。
在函數(shù)內(nèi)部定義的auto變量(沒(méi)有用關(guān)鍵字static定義的變量)的生存期從程序開(kāi)始執(zhí)行其所在的程序塊代碼時(shí)開(kāi)始,到程序離開(kāi)該程序塊時(shí)為止。作為函數(shù)參數(shù)的變量只在調(diào)用該函數(shù)期間存在。這些變量被存儲(chǔ)在棧(stack)中。棧是內(nèi)存中的一段空間,開(kāi)始很小,以后逐漸自動(dòng)增大,直到達(dá)到某個(gè)預(yù)定義的界限。在象DOS這樣的沒(méi)有虛擬內(nèi)存(virtual memory)的系統(tǒng)中,這個(gè)界限由系統(tǒng)決定,并且通常非常大,因此程序員不必?fù)?dān)心用盡??臻g。關(guān)于虛擬內(nèi)存 的討論,請(qǐng)參見(jiàn)2.3。
第三種(也是最后一種)內(nèi)存空間實(shí)際上并不存儲(chǔ)變量,但是可以用來(lái)存儲(chǔ)變量所指向的數(shù)據(jù)。如果把調(diào)用malloc()函數(shù)的結(jié)果賦給一個(gè)指針變量,那么這個(gè)指針變量將包含一塊動(dòng)態(tài)分配的內(nèi)存的地址,這塊內(nèi)存位于一段名為“堆(heap)”的內(nèi)存空間中。堆開(kāi)始時(shí)也很小,但當(dāng)程序員調(diào)用malloc()或calloc()等內(nèi)存分配函數(shù)時(shí)它就會(huì)增大。堆可以和數(shù)據(jù)段或棧共用一個(gè)內(nèi)存段(memorysegment),也可以有它自己的內(nèi)存段,這完全取決于編譯選項(xiàng)和操作系統(tǒng)。
與棧相似,堆也有一個(gè)增長(zhǎng)界限,并且決定這個(gè)界限的規(guī)則與棧相同。
請(qǐng)參見(jiàn):
1.1 什么是局部程序塊(10calblock)?
2.2 變量必須初始化嗎?
2.3 什么是頁(yè)抖動(dòng)(pagethrashing)?
7.20 什么是棧(stack)?
7.21 什么是堆(heap)7 .
2.2 變量必須初始化嗎?
不。使用變量之前應(yīng)該給變量一個(gè)值,一個(gè)好的編譯程序?qū)椭惆l(fā)現(xiàn)那些還沒(méi)有被給定一個(gè)值就被使用的變量。不過(guò),變量不一定需要初始化。在函數(shù)外部定義的變量或者在函數(shù)內(nèi)部用static關(guān)鍵字定義的變量(被定義在數(shù)據(jù)段中的那些變量,見(jiàn)2.1)在沒(méi)有明確地被程序初始化之前都已被系統(tǒng)初始化為0了。在函數(shù)內(nèi)部或程序塊內(nèi)部定義的不帶static關(guān)鍵字的變量都是自動(dòng)變量,如果你沒(méi)有明確地初始化這些變量,它們就會(huì)具有未定義值。如果你沒(méi)有初始化一個(gè)自動(dòng)變量,在使用它之前你就必須保證先給它賦值。
調(diào)用malloc()函數(shù)從堆中分配到的空間也包含未定義的數(shù)據(jù),因此在使用它之前必須先進(jìn)行初始化,但調(diào)用calloc()函數(shù)分配到的空間在分配時(shí)就已經(jīng)被初始化為0了。
請(qǐng)參見(jiàn):
1.1 什么是局部程序塊(10calblock)?
7.20 什么是棧(stack)?
7.21 什么是堆(heap)?
2.3 什么是頁(yè)抖動(dòng)(pagethrashing)?
有些操作系統(tǒng)(如UNIX和增強(qiáng)模式下的Windows)使用虛擬內(nèi)存,這是一種使機(jī)器的作業(yè)地址空間大于實(shí)際內(nèi)存的技術(shù),它是通過(guò)用磁盤(pán)空間模擬RAM(random—access memory)來(lái)實(shí)現(xiàn)的。
在80386和更高級(jí)的Intel CPU芯片中,在現(xiàn)有的大多數(shù)其它微處理器(如Motorola 68030,sparc和Power PC)中,都有一個(gè)被稱(chēng)為內(nèi)存管理單元(Memory Management Unit,縮寫(xiě)為MMU)的器件。MMU把內(nèi)存看作是由一系列“頁(yè)(page)”組成的來(lái)處理。一頁(yè)內(nèi)存是指一個(gè)具有一定大小的連續(xù)的內(nèi)存塊,通常為4096或8192字節(jié)。操作系統(tǒng)為每個(gè)正在運(yùn)行的程序建立并維護(hù)一張被稱(chēng)為進(jìn)程內(nèi)存映射(Process Memory Map,縮與為PMM)的表,表中記錄了程序可以存取的所有內(nèi)存頁(yè)以及它們的實(shí)際位置。
每當(dāng)程序存取一塊內(nèi)存時(shí),它會(huì)把相應(yīng)的地址(虛擬地址,virtualaddress)傳送給MMU,MMU會(huì)在PMM中查找這塊內(nèi)存的實(shí)際位置(物理地址,physical address),物理地址可以是由操作系統(tǒng)指定的在內(nèi)存中或磁盤(pán)上的任何位置。如果程序要存取的位置在磁盤(pán)上,就必須把包含該地址的頁(yè)從磁盤(pán)上讀到內(nèi)存中,并且必須更新PMM以反映這個(gè)變化(這被稱(chēng)為pagefault,即頁(yè)錯(cuò))。
希望你繼續(xù)讀下去,因?yàn)橄旅婢鸵榻B其中的難點(diǎn)了。存取磁盤(pán)比存取RAM要慢得多,所以操作系統(tǒng)會(huì)試圖在RAM中保持盡量多的虛擬內(nèi)存。如果你在運(yùn)行一個(gè)非常大的程序(或者同時(shí)運(yùn)行幾個(gè)小程序),那么可能沒(méi)有足夠的RAM來(lái)承擔(dān)程序要使用的全部?jī)?nèi)存,因此必須把一些頁(yè)從RAM中移到磁盤(pán)上(這被為pagingout,即頁(yè)出)。
操作系統(tǒng)會(huì)試圖去判斷哪些頁(yè)可能暫時(shí)不會(huì)被使用(通常基于過(guò)去使用內(nèi)存的情況),如果它判斷錯(cuò)了,或者程序正在很多地方存取很多內(nèi)存,那么為了讀入已調(diào)出的頁(yè),就會(huì)產(chǎn)生大量頁(yè)錯(cuò)動(dòng)作。因?yàn)镽AM已被全部使用,所以為了調(diào)入要存取的一頁(yè),必須調(diào)出另一頁(yè),而這將導(dǎo)致更多的頁(yè)錯(cuò)動(dòng)作,因?yàn)榇藭r(shí)不同的一頁(yè)已被移到磁盤(pán)上。在短時(shí)間內(nèi)出現(xiàn)大量頁(yè)錯(cuò)動(dòng)作的情形被稱(chēng)為頁(yè)抖動(dòng),它將大大降低系統(tǒng)的執(zhí)行效率。
頻繁存取內(nèi)存中大量散布的位置的程序更容易在系統(tǒng)中造成頁(yè)抖動(dòng)。如果同時(shí)運(yùn)行許多小程序,而實(shí)際上已經(jīng)不再使用這些程序,也很容易造成頁(yè)抖動(dòng)。為了減少頁(yè)抖動(dòng),你應(yīng)該減少同時(shí)運(yùn)行的程序的數(shù)目。對(duì)于大的程序,你應(yīng)該改變它的工作方式,以盡量使操作系統(tǒng)能準(zhǔn)確地判斷出哪些頁(yè)不再需要。為此,你可以使用高速緩沖存儲(chǔ)技術(shù),或者改變用于大型數(shù)據(jù)結(jié)構(gòu)的查找算法,或者使用效率更高的malloc()函數(shù)。當(dāng)然,你也可以考慮增加系統(tǒng)的RAM,以減少頁(yè)出動(dòng)作。
請(qǐng)參見(jiàn):
7.17 怎樣說(shuō)明一個(gè)大于640KB的數(shù)組?
7.21 什么是堆(heap)?
18.14 怎樣才能使DOS程序獲得超過(guò)64KB的可用內(nèi)存?
21.31 Windows是怎樣組織內(nèi)存的?
2.4 什么是const指針?
如果希望一個(gè)變量在被初始化后其值不會(huì)被修改,程序員就會(huì)通過(guò)cons,修飾符和編譯程序達(dá)成默契。編譯程序會(huì)努力去保證這種默契——它將禁止程序中出現(xiàn)對(duì)說(shuō)明為const的變量進(jìn)行修改的代碼。
const指針的準(zhǔn)確提法應(yīng)該是指向const數(shù)據(jù)的指針,即它所指向的數(shù)據(jù)不能被修改。只要在指針說(shuō)明的開(kāi)頭加入const修飾符,就可說(shuō)明一個(gè)cosnt指針。盡管const指針?biāo)赶虻臄?shù)據(jù)不能被修改,但cosnt指針本身是可以修改的。下面給出了const指針的一些合法和非法的用法例子:
const char *str="hello";
char c=*str; /*legal*/
str++; /*legal*/
*str='a'; /* illegal */
str[1]='b'; /*illegal*/
前兩條語(yǔ)句是合法的,因?yàn)樗鼈儧](méi)有修改str所指向的數(shù)據(jù);后兩條語(yǔ)句是非法的,因?yàn)樗鼈円薷膕tr所指向的數(shù)據(jù)。
在說(shuō)明函數(shù)參數(shù)時(shí),常常要使用const指針。例如,一個(gè)計(jì)算字符串長(zhǎng)度的函數(shù)不必改變字符串內(nèi)容,它可以寫(xiě)成這樣:
my_strlen(const char *str)
{
int count=0;
while ( * str++)
{
count ++;
}
return count;
}
注意,如果有必要,一個(gè)非const指針可以被隱式地轉(zhuǎn)換為const指針,但一個(gè)const指針不能被轉(zhuǎn)換成非const指針。這就是說(shuō),在調(diào)用my_strlen()時(shí),它的參數(shù)既可以是一個(gè)const指針,也可以是一個(gè)非const指針。
請(qǐng)參見(jiàn):
2.7 一個(gè)變量可以同時(shí)被說(shuō)明為const和volatile嗎?
2.8 什么時(shí)候應(yīng)該使用const修飾符?
2.14 什么時(shí)候不應(yīng)該使用類(lèi)型強(qiáng)制轉(zhuǎn)換(type cast)?
2. 18 用const說(shuō)明常量有什么好處?
2.5 什么時(shí)候應(yīng)該使用register修飾符?它真的有用嗎?
register修飾符暗示編譯程序相應(yīng)的變量將被頻繁使用,如果可能的話,應(yīng)將其保存在CPU的寄存器中,以加快其存取速度。但是,使用register修飾符有幾點(diǎn)限制。
首先,register變量必須是能被CPU寄存器所接受的類(lèi)型。這通常意味著register變量必須是一個(gè)單個(gè)的值,并且其長(zhǎng)度應(yīng)小于或等于整型的長(zhǎng)度。但是,有些機(jī)器的寄存器也能存放浮點(diǎn)數(shù)。
其次,因?yàn)閞egister變量可能不存放在內(nèi)存中,所以不能用取址運(yùn)算符“&”來(lái)獲取register變量的地址。如果你試圖這樣做,編譯程序就會(huì)報(bào)告這是一個(gè)錯(cuò)誤。
register修飾符的用處有多大還受其它一些規(guī)則的影響。因?yàn)榧拇嫫鞯臄?shù)量是有限的,而且某些寄存器只能接受特定類(lèi)型的數(shù)據(jù)(如指針和浮點(diǎn)數(shù)),因此,真正能起作用的register修飾符的數(shù)目和類(lèi)型都依賴(lài)于運(yùn)行程序的機(jī)器,而任何多余的register修飾符都將被編譯程序所忽略。
在某些情況下,把變量保存在寄存器中反而會(huì)降低運(yùn)行速度,因?yàn)楸徽加玫募拇嫫鞑荒茉儆糜谄渌康模颉咦兞勘皇褂玫拇螖?shù)不夠多,不足以抵消裝入和存儲(chǔ)變量所帶來(lái)的額外開(kāi)銷(xiāo)。
那么,什么時(shí)候應(yīng)該使用register修飾符呢?回答是,對(duì)現(xiàn)有的大多數(shù)編譯程序來(lái)說(shuō),永遠(yuǎn)不要使用register修飾符。早期的C編譯程序不會(huì)把變量保存在寄存器中,除非你命令它這樣做,這時(shí)register修飾符是C語(yǔ)言的一種很有價(jià)值的補(bǔ)充。然而,隨著編譯程序設(shè)計(jì)技術(shù)的進(jìn)步,在決定哪些變量應(yīng)該被存到寄存器中時(shí),現(xiàn)在的C編譯程序能比程序員作出更好的決定。
實(shí)際上,許多C編譯程序會(huì)忽略register修飾符,因?yàn)楸M管它完全合法,但它僅僅是暗示而不是命令。
在極罕見(jiàn)的情況下,程序運(yùn)行速度很慢,而你也知道這是因?yàn)橛幸粋€(gè)變量被存儲(chǔ)在內(nèi)存中,也許你最后會(huì)試圖在該變量前面加上register修飾符,但是,如果這并沒(méi)有加快程序的運(yùn)行速度,你也不要感到奇怪。
請(qǐng)參見(jiàn):
2.6 什么時(shí)候應(yīng)該使用volatile修飾符?
2. 6 什么時(shí)候應(yīng)該使用volatile修飾符?
volatile修飾符告訴編譯程序不要對(duì)該變量所參與的操作進(jìn)行某些優(yōu)化。在兩種特殊的情況下需要使用volatile修飾符:第一種情況涉及到內(nèi)存映射硬件(memory-mapped hardware,如圖形適配器,這類(lèi)設(shè)備對(duì)計(jì)算機(jī)來(lái)說(shuō)就好象是內(nèi)存的一部分一樣),第二種情況涉及到共享內(nèi)存(shared memory,即被兩個(gè)以上同時(shí)運(yùn)行的程序所使用的內(nèi)存)。
大多數(shù)計(jì)算機(jī)擁有一系列寄存器,其存取速度比計(jì)算機(jī)主存更快。好的編譯程序能進(jìn)行一種被稱(chēng)為“冗余裝入和存儲(chǔ)的刪去”(redundant load and store removal)的優(yōu)化,即編譯程序會(huì)·在程序中尋找并刪去這樣兩類(lèi)代碼:一類(lèi)是可以刪去的從內(nèi)存裝入數(shù)據(jù)的指令,因?yàn)橄鄳?yīng)的數(shù)據(jù)已經(jīng)被存放在寄存器中;另一種是可以刪去的將數(shù)據(jù)存入內(nèi)存的指令,因?yàn)橄鄳?yīng)的數(shù)據(jù)在再次被改變之前可以一直保留在寄存器中。
如果一個(gè)指針變量指向普通內(nèi)存以外的位置,如指向一個(gè)外圍設(shè)備的內(nèi)存映射端口,那么冗余裝入和存儲(chǔ)的優(yōu)化對(duì)它來(lái)說(shuō)可能是有害的。例如,為了調(diào)整某個(gè)操作的時(shí)間,可能會(huì)用到下述函數(shù):
time_t time_addition(volatile const struct timer * t, int a),
{
int n
int x
time_t then
x=O;
then= t->value
for (n=O; n<1O00; n++)
{
x=x+a ;
}
return t->value - then;
}
在上述函數(shù)中,變量t->value實(shí)際上是一個(gè)硬件計(jì)數(shù)器,其值隨時(shí)間增加。該函數(shù)執(zhí)行1000次把a(bǔ)值加到x上的操作,然后返回t->value在這1000次加法的執(zhí)行期間所增加的值。
如果不使用volatile修飾符,一個(gè)聰明的編譯程序可能就會(huì)認(rèn)為t->value在該函數(shù)執(zhí)行期間不會(huì)改變,因?yàn)樵摵瘮?shù)內(nèi)沒(méi)有明確地改變t->value的語(yǔ)句。這樣,編譯程序就會(huì)認(rèn)為沒(méi)有必要再次從內(nèi)存中讀入t->value并將其減去then,因?yàn)榇鸢赣肋h(yuǎn)是0。因此,編譯程序可能會(huì)對(duì)該函數(shù)進(jìn)行“優(yōu)化”,結(jié)果使得該函數(shù)的返回值永遠(yuǎn)是0。
如果一個(gè)指針變量指向共享內(nèi)存中的數(shù)據(jù),那么冗余裝入和存儲(chǔ)的優(yōu)化對(duì)它來(lái)說(shuō)可能也是有害的,共享內(nèi)存通常用來(lái)實(shí)現(xiàn)兩個(gè)程序之間的互相通訊,即讓一個(gè)程序把數(shù)據(jù)存到共享的那塊內(nèi)存中,而讓另一個(gè)程序從這塊內(nèi)存中讀數(shù)據(jù)。如果從共享內(nèi)存裝入數(shù)據(jù)或把數(shù)據(jù)存入共享內(nèi)存的代碼被編譯程序優(yōu)化掉了,程序之間的通訊就會(huì)受到影響。
請(qǐng)參見(jiàn):
2.7 一個(gè)變量可以同時(shí)被說(shuō)明為const和volatile嗎?
2.14 什么時(shí)候不應(yīng)該使用類(lèi)型強(qiáng)制轉(zhuǎn)換(typecast)?
2.7 一個(gè)變量可以同時(shí)被說(shuō)明為const和volatile嗎?
可以。const修飾符的含義是變量的值不能被使用了const修飾符的那段代碼修改,但這并不意味著它不能被這段代碼以外的其它手段修改。例如,在2.6的例子中,通過(guò)一個(gè)volatile const指針t來(lái)存取timer結(jié)構(gòu)。函數(shù)time_addition()本身并不修改t->value的值,因此t->value被說(shuō)明為const。不過(guò),計(jì)算機(jī)的硬件會(huì)修改這個(gè)值,因此t->value又被說(shuō)明為volatile。如果同時(shí)用const和volatile來(lái)說(shuō)明一個(gè)變量,那么這兩個(gè)修飾符隨便哪個(gè)在先都行,
請(qǐng)參見(jiàn):
2.6什么時(shí)候應(yīng)該使用volatile修飾符?
2.8什么時(shí)候應(yīng)該使用const修飾符?
2.14什么時(shí)候不應(yīng)該使用類(lèi)型強(qiáng)制轉(zhuǎn)換(typecast)?
2.8 什么時(shí)候應(yīng)該使用const修飾符?
使用const修飾符有幾個(gè)原因,第一個(gè)原因是這樣能使編譯程序找出程序中不小心改變變量值的錯(cuò)誤。請(qǐng)看下例:
while ( * str=0) / * programmer meant to write * str! =0 * /
{
/ * some code here * /
strq++;
}
其中的“=”符號(hào)是輸入錯(cuò)誤。如果在說(shuō)明str時(shí)沒(méi)有使用const修飾符,那么相應(yīng)的程序能通過(guò)編譯但不能被正確執(zhí)行。
第二個(gè)原因是效率。如果編譯程序知道某個(gè)變量不會(huì)被修改,那么它可能會(huì)對(duì)生成的代碼進(jìn)行某些優(yōu)化。
如果一個(gè)函數(shù)參數(shù)是一個(gè)指針,并且你不希望它所指向的數(shù)據(jù)被該函數(shù)或該函數(shù)所調(diào)用的函數(shù)修改,那么你應(yīng)該把該參數(shù)說(shuō)明為const指針。如果一個(gè)函數(shù)參數(shù)通過(guò)值(而不是通過(guò)指針)被傳遞給函數(shù),并且你不希望其值被該函數(shù)所調(diào)用的函數(shù)修改,那么你應(yīng)該把該參數(shù)說(shuō)明為const。然而,在實(shí)際編程中,只有在編譯程序通過(guò)指針存取這些數(shù)據(jù)的效率比拷貝這些數(shù)據(jù)更高時(shí),才把這些參數(shù)說(shuō)明為const。
請(qǐng)參見(jiàn):
2.7 一個(gè)變量可以同時(shí)被說(shuō)明為const和volatile嗎?
2.14 什么時(shí)候不應(yīng)該使用類(lèi)型強(qiáng)制轉(zhuǎn)換(typecast)?
2.18用const說(shuō)明常量有什么好處?
2.9 浮點(diǎn)數(shù)比較(floating-point comparisons)的可靠性如何?
浮點(diǎn)數(shù)是計(jì)算機(jī)編程中的“魔法(black art)”,原因之一是沒(méi)有一種理想的方式可以表示一個(gè)任意的數(shù)字。電子電氣工程協(xié)會(huì)(IEEE)已經(jīng)制定出浮點(diǎn)數(shù)的表示標(biāo)準(zhǔn),但你不能保證所使用的每臺(tái)機(jī)器都遵循這一標(biāo)準(zhǔn)。
即使你使用的機(jī)器遵循這一標(biāo)準(zhǔn),還存在更深的問(wèn)題。從數(shù)學(xué)意義上講,兩個(gè)不同的數(shù)字之間有無(wú)窮個(gè)實(shí)數(shù)。計(jì)算機(jī)只能區(qū)分至少有一位(bit)不同的兩個(gè)數(shù)字。如果要表示那些無(wú)窮無(wú)盡的各不相同的數(shù)字,就要使用無(wú)窮數(shù)目的位。計(jì)算機(jī)只能用較少的位(通常是32位或64位)來(lái)表示一個(gè)很大的范圍內(nèi)的數(shù)字,因此它只能近似地表示大多數(shù)數(shù)字。
由于浮點(diǎn)數(shù)是如此難對(duì)付,因此比較一個(gè)浮點(diǎn)數(shù)和某個(gè)值是否相等或不等通常是不好的編程習(xí)慣。但是,判斷一個(gè)浮點(diǎn)數(shù)是否大于或小于某個(gè)值就安全多了。例如,如果你想以較小的步長(zhǎng)依次使用一個(gè)范圍內(nèi)的數(shù)字,你可能會(huì)編寫(xiě)這樣一個(gè)程序:
#include <stdio.h>
const float first = O.O;
const float last = 70.0
const float small= O.007
main ( )
{
float f;
for (f=first; f !=last && f<last+1.O; f +=small)
printf("f is now %g\n", f);
}
然而,舍入誤差(rounding error)和變量small的表示誤差可能導(dǎo)致f永遠(yuǎn)不等于last(f可能會(huì)從稍小于last的一個(gè)數(shù)增加到一個(gè)稍大于last的數(shù)),這樣,循環(huán)會(huì)跳過(guò)last。加入不等式"f<last+1.0"就是為了防止在這種情況發(fā)生后程序繼續(xù)運(yùn)行很長(zhǎng)時(shí)間。如果運(yùn)行該程序并且被打印出來(lái)的f值是71或更大的數(shù)值,就說(shuō)明已經(jīng)發(fā)生了這種情況。
一種較安全的方法是用不等式"f<last"作為條件來(lái)終止循環(huán),例如:
float f;
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -