“語言的作用域規(guī)則”是一組確定一部分代碼是否“可見”或可訪問另一部分代碼和數(shù)據(jù)的規(guī)則。
C語言中的每一個函數(shù)都是一個獨立的代碼塊。一個函數(shù)的代碼塊是隱藏于函數(shù)內(nèi)部的,不能被任何其它函數(shù)中的任何語句(除調(diào)用它的語句之外)所訪問(例如,用g o t o語句跳轉(zhuǎn)到另一個函數(shù)內(nèi)部是不可能的)。構(gòu)成一個函數(shù)體的代碼對程序的其它部分來說是隱蔽的,它既不能影響程序其它部分,也不受其它部分的影響。換言之,由于兩個函數(shù)有不同的作用域,定義在一個函數(shù)內(nèi)部的代碼數(shù)據(jù)無法與定義在另一個函數(shù)內(nèi)部的代碼和數(shù)據(jù)相互作用。
C語言中所有的函數(shù)都處于同一作用域級別上。這就是說,把一個函數(shù)定義于另一個函數(shù)內(nèi)部是不可能的。
4.2.1 局部變量
在函數(shù)內(nèi)部定義的變量成為局部變量。在某些C語言教材中,局部變量稱為自動變量,這就與使用可選關(guān)鍵字a u t o定義局部變量這一作法保持一致。局部變量僅由其被定義的模塊內(nèi)部的語句所訪問。換言之,局部變量在自己的代碼模塊之外是不可知的。切記:模塊以左花
括號開始,以右花括號結(jié)束。
對于局部變量,要了解的最重要的東西是:它們僅存在于被定義的當前執(zhí)行代碼塊中,即局部變量在進入模塊時生成,在退出模塊時消亡。
定義局部變量的最常見的代碼塊是函數(shù)。例如,考慮下面兩個函數(shù)。
整數(shù)變量x被定義了兩次,一次在func1()中,一次在func2()中。func1()和func2()中的x互不相關(guān)。其原因是每個x作為局部變量僅在被定義的塊內(nèi)可知。
語言中包括了關(guān)鍵字auto,它可用于定義局部變量。但自從所有的非全局變量的缺省值假定為auto以來,auto就幾乎很少使用了,因此在本書所有的例子中,均見不到這一關(guān)鍵字。
在每一函數(shù)模塊內(nèi)的開始處定義所有需要的變量,是最常見的作法。這樣做使得任何人讀此函數(shù)時都很容易,了解用到的變量。但并非必須這樣做不可,因為局部變量可以在任何模塊中定義。為了解其工作原理,請看下面函數(shù)。
這里的局部變量s就是在if塊入口處建立,并在其出口處消亡的。因此s僅在if塊中可知,而在其它地方均不可訪問,甚至在包含它的函數(shù)內(nèi)部的其它部分也不行。
在一個條件塊內(nèi)定義局部變量的主要優(yōu)點是僅在需要時才為之分配內(nèi)存。這是因為局部變量僅在控制轉(zhuǎn)到它們被定義的塊內(nèi)時才進入生存期。雖然大多數(shù)情況下這并不十分重要,但當代碼用于專用控制器(如識別數(shù)字安全碼的車庫門控制器)時,這就變得十分重要了,因為這時隨機存儲器(RAM)極其短缺。
由于局部變量隨著它們被定義的模塊的進出口而建立或釋放,它們存儲的信息在塊工作結(jié)束后也就丟失了。切記,這點對有關(guān)函數(shù)的訪問特別重要。當訪問一函數(shù)時,它的局部變量被建立,當函數(shù)返回時,局部變量被銷毀。這就是說,局部變量的值不能在兩次調(diào)用之間保持。
4.2.2全局變量
與局部變量不同,全局變量貫穿整個程序,并且可被任何一個模塊使用。它們在整個程序執(zhí)行期間保持有效。全局變量定義在所有函數(shù)之外,可由函數(shù)內(nèi)的任何表達式訪問。在下面的程序中可以看到,變量count定義在所有函數(shù)之外,函數(shù)main()之前。但其實它可以放置在任何第一次被使用之前的地方,只要不在函數(shù)內(nèi)就可以。實踐表明,定義全局變量的最佳位置是在程序的頂部。
仔細研究此程序后,可見變量count既不是main()也不是func1()定義的,但兩者都可以使用它。函數(shù)func2()也定義了一個局部變量count。當func2訪問count時,它僅訪問自己定義的局部變量count,而不是那個全局變量count。切記,全局變量和某一函數(shù)的局部變量同名時,該函數(shù)對該名的所有訪問僅針對局部變量,對全局變量無影響,這是很方便的。然而,如果忘記了這點,即使程序看起來是正確的,也可能導致運行時的奇異行為。
全局變量由C編譯程序在動態(tài)區(qū)之外的固定存儲區(qū)域中存儲。當程序中多個函數(shù)都使用同一數(shù)據(jù)時,全局變量將是很有效的。然而,由于三種原因,應(yīng)避免使用不必要的全局變量:
①不論是否需要,它們在整個程序執(zhí)行期間均占有存儲空間。②由于全局變量必須依靠外部定義,所以在使用局部變量就可以達到其功能時使用了全局變量,將降低函數(shù)的通用性,這是因為它要依賴其本身之外的東西。③大量使用全局變量時,不可知的和不需要的副作用將
可能導致程序錯誤。如在編制大型程序時有一個重要的問題:變量值都有可能在程序其它地點偶然改變。
結(jié)構(gòu)化語言的原則之一是代碼和數(shù)據(jù)的分離。C語言是通過局部變量和函數(shù)的使用來實現(xiàn)這一分離的。下面用兩種方法編制計算兩個整數(shù)乘積的簡單函數(shù)mul()。
通用的專用的
mul(x,y) intx,y;
intx,y; mul()
{{
return(x*y);return(x*y);
}}
兩個函數(shù)都是返回變量x和y的積,可通用的或稱為參數(shù)化版本可用于任意兩整數(shù)之積,而專用的版本僅能計算全局變量x和y的乘積。
4.2.3動態(tài)存儲變量
從變量的作用域原則出發(fā),我們可以將變量分為全局變量和局部變量;換一個方式,從變量的生存期來分,可將變量分為動態(tài)存儲變量及靜態(tài)存儲變量。
動態(tài)存儲變量可以是函數(shù)的形式參數(shù)、局部變量、函數(shù)調(diào)用時的現(xiàn)場保護和返回地址。
這些動態(tài)存儲變量在函數(shù)調(diào)用時分配存儲空間,函數(shù)結(jié)束時釋放存儲空間。動態(tài)存儲變量的定義形式為在變量定義的前面加上關(guān)鍵字“auto”,例如:
auto int a,b,c;
“auto”也可以省略不寫。事實上,我們已經(jīng)使用的變量均為省略了關(guān)鍵字“auto”的動態(tài)存儲變量。有時我們甚至為了提高速度,將局部的動態(tài)存儲變量定義為寄存器型的變量,定義的形式為在變量的前面加關(guān)鍵字“register”,例如:
register int x,y,z;
這樣一來的好處是:將變量的值無需存入內(nèi)存,而只需保存在CPU內(nèi)的寄存器中,以使速度大大提高。由于CPU內(nèi)的寄存器數(shù)量是有限的,不可能為某個變量長期占用。因此,一些操作系統(tǒng)對寄存器的使用做了數(shù)量的限制?;蚨嗷蛏伲蚋静惶峁?,用自動變量來替代。
4.2.4靜態(tài)存儲變量
在編譯時分配存儲空間的變量稱為靜態(tài)存儲變量,其定義形式為在變量定義的前面加上關(guān)鍵字“static”,例如:
static int a=8;
定義的靜態(tài)存儲變量無論是做全程量或是局部變量,其定義和初始化在程序編譯時進行。
作為局部變量,調(diào)用函數(shù)結(jié)束時,靜態(tài)存儲變量不消失并且保留原值。
從上述程序看,函數(shù)f()被三次調(diào)用,由于局部變量x是靜態(tài)存儲變量,它是在編譯時分配存儲空間,故每次調(diào)用函數(shù)f()時,變量x不再重新初始化,保留加1后的值,得到上面的輸出。