C++中static extern關鍵字及全局變量的辨析

全局變量,就是指那些定義在函數或類之外的變量,它可作爲多個類或函數都需要共同使用的變量。
(1)全局變量會被自動初始化,函數中的變量不會被自動初始化,類中定義的那些成員變量(內建)也不會自動初始化。那麼這裏有一個疑問,爲什麼要這樣設置?並且爲什麼,進程內存區中,分爲初始化了的全局變量和靜態變量,和未初始化的全局和靜態變量。
(2)全局變量如果加上static關鍵字,事情將會變得很奇妙。
我們知道,全局變量理論上是指應用程序級別的全局。普通的全局變量是面向整個程序的,當各個文件各自編譯,然後鏈接成一個可執行程序之後,全局變量的確是被所有文件中的“看到的”,但是,如果想讓別的文件在編譯程序的時候就能夠看到,(如果你不做然後措施,直接使用另外一個文件中定義的變量,那將通不過編譯)
有兩個辦法:
第一個就是#include方法,但是這個方法,其實是將文件合併成一個文件,並沒有正面回答。
第二個就是在要使用該變量的文件中聲明(extern)一個外部變量,這樣等於就是告訴編譯器,“該變量是有的,只是在另外一個文件中,等到鏈接的時候你就可以看到了。”這個extern必須要加,否則就是重新定義個變量,到時候連接會出現重定義問題。
但是,如果你在全局變量前加上static,那麼它的作用範圍就變小了,編程了文件範圍。這就會導致該變量是不容許別的文件通過extern聲明方式來操作。因爲該變量對其它文件是不可見的。
我們所說的變量的生命週期和作用域時我們有這樣的說法:
1) 全局變量和靜態變量的生命週期爲整個進程,他們都處在內存的同一個區域
2) 普通全局變量的作用域爲全局(整個軟件,可跨域文件,其他文件想使用可以使用extern聲明),函數中的靜態變量的作用域爲該函數。
3) 靜態全局變量,的作用域則被限制在該文件中,所以,這個時候其他文件通過extern想使用該文件是不行的,(就算成功啦,那是因爲你引用了其他文件中的定義)。同時,由於作用域被限制在當前文件,所以,不同的文件定義自己的全局變量就不會擔心與其他文件出現衝突。

 

一直以來,有一個有關全局變量定義的標準:
1) 如果在這個文件中定義的全局變量不打算給別人用,那麼你就將它定義爲static全局變量吧!因爲這樣你不必擔心其他文件也定義了一個同名變量,在連接的時候出現重定義。
2) 如果你的全局變量是打算給其他文件使用的,那麼就不要加上static,因爲這樣在其他文件中可以使用extern對該定義進行引用。
3) 這麼說來,static 和extern是不能同時用來修飾一個變量的,extern修飾表示該變量只是聲明,聲明它使用了其他文件的變量定義,static的修飾表示我這個變量(自己定義的),只能被當前文件訪問。兩者完全衝突,所以編譯器會報錯——‘n’的聲明中有相互衝突的限定符。

函數或變量在聲明時,並沒有給它實際的物理內存空間,它有時候可以保證你的程序編譯通過, 但是當函數或變量定義的時候,它就在內存中有了實際的物理空間,如果你在編譯模塊中引用的外部變量沒有在整個工程中任何一個地方定義的話, 那麼即使它在編譯時可以通過,在連接時也會報錯,因爲程序在內存中找不到這個變量!你也可以這樣理解, 對同一個變量或函數的聲明可以有多次,而定義只能有一次!

在IDE開發工具大行其道的今天,對於編譯的一些概念很多人已經不再清楚了,很多程序員最怕的就是處理連接錯誤(LINK ERROR), 因爲它不像編譯錯誤那樣可以給出你程序錯誤的具體位置,你常常對這種錯誤感到懊惱,但是如果你經常使用gcc,makefile等工具在linux或者嵌 入式下做開發工作的話,那麼你可能非常的理解編譯與連接的區別!當在VC這樣的開發工具上編寫完代碼,點擊編譯按鈕準備生成exe文件時,VC其實做了兩 步工作,第一步,將每個.cpp(.c)和相應.h文件編譯成obj文件;第二步,將工程中所有的obj文件進行LINK生成最終的.exe文件,那麼錯 誤就有可能在兩個地方產生,一個是編譯時的錯誤,這個主要是語法錯誤,另一個是連接錯誤,主要是重複定義變量等。我們所說的編譯單元就是指在編譯階段生成 的每個obj文件,一個obj文件就是一個編譯單元,也就是說一個cpp(.c)和它相應的.h文件共同組成了一個編譯單元,一個工程由很多個編譯單元組 成,每個obj文件裏包含了變量存儲的相對地址等 。

 

Extern有兩種用法:

(1) C一起使用 extern C ,來在C++中表達,在編譯的時候按照C的風格進行編譯。

(2) 於是普通修飾全局變量的聲明,extern int n;)注意它只能是聲明,不能定義初始化,它聲明表示,該變量可以從另外一個文件中找到,現在大膽使用就行了。到時候(鏈接的時候)就可以找到了,所以我認爲這個extern關鍵是來支持,當前文件可以通過編譯器的檢查。當前文件使用了另外一個文件內定義的東西,但是有不能直接#include它,所以這導致這種技術的出現。注意,必須有一個文件定義了該變量(int n;)如果沒有一個文件定義了它,那麼編譯的時候能夠通過,但是在連接的時候,文件找不到你給它的承諾,所以就會報錯——undefined reference to `n'
    當extern不與"C"在一起修飾變量或函數時,如在頭文件中: extern int g_Int; 它的作用就是聲明函數或全局變量的作用範圍的關鍵字,其聲明的函數和變量可以在本模塊或者其他模塊中使用,記住它是一個聲明不是定義!也就是說B模塊(編譯 單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可, 在編譯階段,模塊B雖然找不到該函數或變量,但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。

Static的應用場景:

(1) C++面向對象中修飾成員函數或變量,這表明它所屬範圍爲這個類,而不是對象。

 其實,這個時候,我們完全可以,將他理解爲,使用範圍限定在類中的,全局變量.(其實完全就是這樣,靜態變量和全局變量,其實是聯繫很緊密的,在內存中,他們存儲的位置是一樣的,生命週期也是一樣的,關鍵在於他們的可視範圍發生的變化而已。

(2) 在局部變量中(函數中)修飾變量,表示該變量是這個函數範圍內的不變的,而不是隨着每一次的函數被調用而更新。所以你會發現,static修飾函數中的變量和修飾類中的成員有異曲同工之妙。函數每次被調用,類似於類每次被實例化。普通的成員都會是一個新的,而static成員與至始至終屬於該函數(類)。

(3) 在全局變量中修飾變量,表示該變量的作用範圍爲當前文件,而不是整個程序。

 

static全局變量與普通的全局變量有什麼區別 ? static局部變量和普通局部變量有什麼區別? static函數與普通函數有什麼區別?

  全局變量(外部變量)的說明之前再冠以static 就構成了靜態的全局變量。全局變量本身就是靜態存儲方式, 靜態全局變量當然也是靜態存儲方式。 這兩者在存儲方式上並無不同。這兩者的區別雖在於非靜態全局變量的作用域是整個源程序 當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。 而靜態全局變量則限制了其作用域, 即只在定義該變量的源文件內有效, 在同一源程序的其它源文件中不能 使用它。由於靜態全局變量的作用域侷限於一個源文件內,只

能爲該源文件內的函數公用, 因此可以避免在其它源文件中引起錯誤。

  從以上分析可以看出, 局部變量改變爲靜態變量後是改變了它的存儲方式即改變了它的生存期。把全局變量改變爲靜態變量後是改變了它的作用域, 限制了它的使用範圍

  static函數與普通函數作用域不同。僅在本文件。只在當前源文件中使用的函數應該說明爲內部函數(static),內部函數應該在當前源文件中說明和定義。對於可在當前源文件以外使用的函數,應該在一個頭文件中說明,要使用這些函數的源文件要包含這個頭文件

  static全局變量與普通的全局變量有什麼區別:static全局變量只初使化一次,防止在其他文件單元中被引用;

   static局部變量和普通局部變量有什麼區別:static局部變量只被初始化一次,下一次依據上一次結果值;

  static函數與普通函數有什麼區別:static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝

 

 

 

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