變量的聲明與定義以及區別

ANSI C規定:變量必須先定義、後使用,因此當用C定義變量時,不僅需要指定變量名,而且還必須告訴編譯器其存儲的數據類型,變量類型告訴編譯器應該在內存中爲變量名分配多大的存儲單元,用來存放相應變量的值(變量值),而變量僅僅是存儲單元的別名,供變量使用的最小存儲單元是字節(Byte)。

由此可見,每個變量都佔據一個特定的位置,每個存儲單元的位置都由“地址”唯一確定並引用,就像一條街道上的房子由它們的門牌號碼標識一樣。即從變量中取值就是通過變量名找到相應的存儲地址,然後讀取該存儲單元中的值,而寫一個變量就是將變量的值存放到與之相應的存儲地址中去。

由於變量的定義不是可執行代碼,因此要求局部變量的定義必須位於用“{}包圍的程序塊”的開頭,即在可執行代碼的前面。比如:

     int     LowerLimit = 80;                                       // 定義LowerLimit爲整型變量

    即在定義LowerLimitint類型數據時,系統就已經爲變量LowerLimit分配了存儲單元。請注意區分變量名和變量值這兩個不同的概念,其中,LowerLimit爲變量名,80爲變量LowerLimit的值,即存放在變量LowerLimit的存儲單元中的數據。

那麼到底如何獲得變量的地址呢?C語言使用“&(地址運算符)加變量名”的方式獲取變量的地址,比如,&LowerLimit就代表變量LowerLimit的地址,詳見後續相關章節的描述。

一個定義只能指定一種變量類型,雖然後面所帶的變量表可以包含一個或多個該類型的變量:

     int    LowerLimit , UpperLimit , sum;                                                            

但如果將一個定義語句中的多個變量拆開在多個定義語句中定義的話:

          int    LowerLimit;                                                                                       // LowerLimit爲數據下限

      int    UppeLimit;                                            // UpperLimit爲數據上限

      int    sum;                                                 //  sum爲求和的結果

則可在各個定義語句中添加註釋,則大大提高了程序的可閱讀性,而且修改起來更加方便,但C編譯器會忽略在每行右邊用於描述變量用途的註釋語句。與此同時還可以在定義中,對變量進行初始化,即允許在變量名的後面緊跟一個等號以及一個表達式。

int    LowerLimit = 1;

int   UpperLimit = LowerLimit+50;

     int   sum; 

       2.外部變量的聲明

      由於變量LowerLimit是在別的文件中定義的,那麼只要在引用之前用關鍵字extern對該變量作“外部變量聲明”即可,無需再爲變量LowerLimit分配存儲單元,這種情況稱之爲聲明或引用性聲明。比如:

 extern   int     LowerLimit;                               // 將已定義的外部變量LowerLimit的作用域擴展到此

在這裏將extern置於變量前,就是爲了告訴編譯器變量LowerLimit這個名字已經在別的文件中被定義了,因此遇到此變量時,需在其它模塊中尋找與之相應的定義。 

有時程序是由多個源程序文件組成的,那麼只需要在其中任一個文件中定義外部變量LowerLimit,而在另一個文件中用extern對變量LowerLimit作“外部變量聲明”,即可將外部變量的作用域擴展到其它文件。

extern用於聲明外部變量時,則類型名可寫也可不寫,由於聲明變量不是定義變量,則可以不指定類型,只需寫出變量名即可。比如:

extern    LowerLimit;                                                  

注意extern不僅可用於聲明變量,而且還可以用於聲明數組和指針,以及用於聲明外部函數,詳見後續相關章節的描述。

       3.靜態變量的定義

當用static修飾變量時,則全局變量與局部變量都保存在內存的靜態區。即便函數運行結束,而靜態變量的值都不會被銷燬,以後仍然還可以繼續使用。

1)靜態全局(外部)變量

       static用於定義外部變量時,則變量僅被所定義的文件使用,而其它文件則無法使用它。比如:

     static unsigned char      __GucTask0;                // 任務0測試變量

         使用靜態全局變量的好處就是在進行模塊化程序開發時,不用擔心外部變量名的衝突,只需在每個文件中定義全局變量時加上static即可。

2)靜態局部變量

靜態局部變量則是在函數體內定義的,那麼只能在這個函數中使用。

       注意static還可以用於修飾函數,如果在函數前加static,則該函數只能成爲被本文件中的其它函數所調用的靜態函數(內部函數),其好處就是不用擔心函數名的衝突,便於模塊化開發,

   4.常變量的聲明

爲了提高程序的可閱讀性與可維護性,ANSI C允許用戶命名常量named constant,聲明爲const的變量)。

當它被初始化之後,它的值便不能改變,因此const主要用於聲明其值不會修改的變量。

ANSI C規定:可以使用const關鍵字聲明常量,修飾符const可以用在類型說明符前,也可以用在類型說明符後。比如:

int  const   MAX_LENGTH = 78;                   // 命名常變量的最佳方式是使用大寫字母

const   int  MAX_LENGTH = 78;                    // MAX_LENGTH的初值爲78

雖然const修飾MAX_LENGTH的值是常量,而實際上MAX_LENGTH卻是一個讀變量。編譯器僅給出了與只

讀變量MAX_LENGTH對應的內存地址,卻並沒有爲只讀變量MAX_LENGTH分配存儲單元,而是將它們保存在符號表中,在編譯時直接進行替換優化。因此使用const修飾常變量,可以節省空間,避免了不必要的內存分配,而且執行效率更高。  

 既然使用const也可以定義常量,那麼它與符號常量到底有什麼區別呢?

 由於const定義的常量有數據類型,因此編譯器會對用const聲明的只讀變量進行類型校驗,以減少出錯的機率;雖然可以使用#define指令定義符號常量,但它在預編譯進行字符替換之後,符號常量就不存在了,因爲#define宏定義的立即數是沒有類型的。很多開發環境只能調試const聲明的常量,而不支持#define。由此可見,const#define聲明常量更有優勢。因此const的引入不僅消除預編譯指令的缺點,而且繼承了預編譯指令的優點。

  雖然在很多時候const#define的優化,但有時#defineconst有優勢,因爲#define不僅聲明常量,而且還可以聲明“帶宏的參數”,這是const無法做到的,所以說const相對於#define的優勢僅限定在聲明常量上。

    注意const除了可以修飾只讀變量之外,還可以用於修飾數組、指針、函數的參數與函數的返回值,詳見後續相關章節的描述。 

5. 標識符

ANSI C規定標識符只能由字母、數字和下劃線3種字符組成,且第1個字符必須爲字母或下劃線。比如:

__GucTask0

注意C是區分大小寫字母的語言,也就是說,由相同字母組成的字符,如果大小寫不同,就會被看做不同的字符。比如,命名agesum的變量與AgeAGE以及Sum的變量就是不同的變量。一般來說,變量名常用小寫字母來表示,比較符合人們的閱讀習慣。

雖然ANSI C並沒有規定標識符的長度,但各個編譯器都有自己的規定,比如,Turbo C則允許變量名最多不超過32個字母。

    關鍵知識點:變量的聲明與定義   

l         聲明與定義的區別

    廣義地說,聲明包含定義,但並非所有的聲明都是定義。對於“int lower_limit;”來說,它既是聲明又是定義;而對“extern lower_limit;”來說,它是聲明不是定義。

“聲明”僅僅告訴編譯器變量名的值的類型而已,不會生成目標代碼,當然也不會給它分配存儲空間,更不會增大可執行程序的體積。由於聲明並不分配存儲空間,因此同一個聲明可以在程序中多次出現。它的位置可在執行代碼之外,也可在執行代碼裏面。

而“定義”不僅要告訴編譯器變量名的值的類型,而且還要給變量分配存儲空間。既然在定義變量時就已經建立了存儲空間,那麼變量的定義只能出現一次,且它的位置在所有執行代碼之外。

    由於系統是根據外部變量的“定義”來分配存儲空間的,因此對外部變量的初始化只能在“定義”時進行,不能在“聲明”中進行,而“聲明”則是告訴編譯器,該變量已經存在。 

l         變量與函數的多源文件共享

當希望在多個源文件中共享變量或函數時,需要確保定義與聲明的一致性。最好的安排是在某個相關的“.c”文件中定義,然後在“.h”文件(頭文件)中進行外部聲明,在需要使用的時候,只要包含對應的頭文件即可。定義變量的“.c”文件也應該包含該頭文件,以便編譯檢查定義和聲明的一致性    

變量的聲明有兩種情況:

1、一種是需要建立存儲空間的。例如:int a 在聲明的時候就已經建立了存儲空間。

2、另一種是不需要建立存儲空間的。 例如:extern int a 其中變量a是在別的文件中定義的。

前者是“定義性聲明(defining declaration)”或者稱爲“定義(definition)”,而後者是“引用性聲明(referncing declaration)”,從廣義的角度來講聲明中包含着定義,即定義是聲明的一個特例,所以並非所有的聲明都是定義,例如:int a 它既是聲明,同時又是定義。然而對於 extern a 來講它只是聲明不是定義。一般的情況下我們常常這樣敘述,把建立空間的聲明稱之爲“定義”,而把不需要建立存儲空間的聲明稱之爲“聲明”。很明顯我們在這裏指的聲明是範圍比較窄的,即狹義上的聲明,也就是說非定義性質的聲明,例如:在主函數中:

int main() {
extern int A;
//這是個聲明而不是定義,聲明A是一個已經定義了的外部變量
//注意:聲明外部變量時可以把變量類型去掉如:extern A;
dosth(); //執行函數
}
int A; //是定義,定義了A爲整型的外部變量

外部變量的“定義”與外部變量的“聲明”是不相同的,外部變量的定義只能有一次,它的位置是在所有函數之外,而同一個文件中的外部變量聲明可以是多次的,它可以在函數之內(哪個函數要用就在那個函數中聲明)也可以在函數之外(在外部變量的定義點之前)。系統會根據外部變量的定義(而不是根據外部變量的聲明)分配存儲空間的。對於外部變量來講,初始化只能是在“定義”中進行,而不是在“聲明”中。所謂的“聲明”,其作用,是聲明該變量是一個已在後面定義過的外部變量,僅僅是爲了“提前”引用該變量而作的“聲明”而已。extern 只作聲明,不作任何定義。

(我們聲明的最終目的是爲了提前使用,即在定義之前使用,如果不需要提前使用就沒有單獨聲明的必要,變量是如此,函數也是如此,所以聲明不會分配存儲空間,只有定義時纔會分配存儲空間。)

用static來聲明一個變量的作用有二:

(1)對於局部變量用static聲明,則是爲該變量分配的空間在整個程序的執行期內都始終存在。

(2)外部變量用static來聲明,則該變量的作用只限於本文件模塊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章