1. 程式人生 > >全域性變數與區域性變數的區別

全域性變數與區域性變數的區別

一、變數的分類 變數可以分為:全域性變數、靜態全域性變數、靜態區域性變數和區域性變數。

按儲存區域分,全域性變數、靜態全域性變數和靜態區域性變數都存放在記憶體的靜態儲存區域,區域性變數存放在記憶體的棧區。 按作用域分,全域性變數在整個工程檔案內都有效;靜態全域性變數只在定義它的檔案內有效;靜態區域性變數只在定義它的函式內有效,只是程式僅分配一次記憶體,函式返回後,該變數不會消失;區域性變數在定義它的函式內有效,但是函式返回後失效。 全域性變數和靜態變數如果沒有手工初始化,則由編譯器初始化為0。區域性變數的值不可知。

靜態全域性變數,只本檔案可以用。

全域性變數是沒有定義儲存型別的外部變數,其作用域是從定義點到程式結束.省略了儲存型別符,系統將預設為是自動型.

靜態全域性變數是定義儲存型別為靜態型的外部變數,其作用域是從定義點到程式結束,所不同的是儲存型別決定了儲存地點,靜態型變數是存放在記憶體的資料區中的,它們在程式開始執行前就分配了固定的位元組,在程式執行過程中被分配的位元組大小是不改變的.只有程式執行結束後,才釋放所佔用的記憶體.

自動型變數存放在堆疊區中.堆疊區也是記憶體中一部分,該部分記憶體在程式執行中是重複使用的. 二、介紹 變數的作用域

  在討論函式的形參變數時曾經提到, 形參變數只在被呼叫期間才分配記憶體單元,呼叫結束立即釋放。 這一點表明形參變數只有在函式內才是有效的, 離開該函式就不能再使用了。這種變數有效性的範圍稱變數的作用域。不僅對於形參變數, C語言中所有的量都有自己的作用域。變數說明的方式不同,其作用域也不同。 C語言中的變數,按作用域範圍可分為兩種, 即區域性變數和全域性變數。

  一、區域性變數

  區域性變數也稱為內部變數。區域性變數是在函式內作定義說明的。其作用域僅限於函式內, 離開該函式後再使用這種變數是非法的。

  例如:

int f1(int a) { int b,c; …… }a,b,c作用域 int f2(int x) { int y,z; }x,y,z作用域 main() { int m,n; }   m,n作用域 在函式f1內定義了三個變數,a為形參,b,c為一般變數。在 f1的範圍內a,b,c有效,或者說a,b,c變數的作用域限於f1內。同理,x,y,z的作用域限於f2內。 m,n的作用域限於main函式內。關於區域性變數的作用域還要說明以下幾點:

  1. 主函式中定義的變數也只能在主函式中使用,不能在其它函式中使用。同時,主函式中也不能使用其它函式中定義的變數。因為主函式也是一個函式,它與其它函式是平行關係。這一點是與其它語言不同的,應予以注意。

  2. 形參變數是屬於被調函式的區域性變數,實參變數是屬於主調函式的區域性變數。

  3. 允許在不同的函式中使用相同的變數名,它們代表不同的物件,分配不同的單元,互不干擾,也不會發生混淆。如在例5.3 中,形參和實參的變數名都為n,是完全允許的。4. 在複合語句中也可定義變數,其作用域只在複合語句範圍內。例如:

main() { int s,a; …… { int b; s=a+b; ……b作用域 } ……s,a作用域 }[例5.11]main() { int i=2,j=3,k; k=i+j; { int k=8; if(i==3) printf(“%d\n”,k); } printf(“%d\n%d\n”,i,k); } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i=3) printf(“%d\n”,k); } printf(“%d\n%d\n”,i,k); }

  本程式在main中定義了i,j,k三個變數,其中k未賦初值。 而在複合語句內又定義了一個變數k,並賦初值為8。應該注意這兩個k不是同一個變數。在複合語句外由main定義的k起作用,而在複合語句內則由在複合語句內定義的k起作用。因此程式第4行的k為main所定義,其值應為5。第7行輸出k值,該行在複合語句內,由複合語句內定義的k起作用,其初值為8,故輸出值為8,第9行輸出i,k值。i是在整個程式中有效的,第7行對i賦值為3,故以輸出也為3。而第9行已在複合語句之外,輸出的k應為main所定義的k,此k值由第4 行已獲得為5,故輸出也為5。

  二、全域性變數

  全域性變數也稱為外部變數,它是在函式外部定義的變數。 它不屬於哪一個函式,它屬於一個源程式檔案。其作用域是整個源程式。在函式中使用全域性變數,一般應作全域性變數說明。 只有在函式內經過說明的全域性變數才能使用。全域性變數的說明符為extern。 但在一個函式之前定義的全域性變數,在該函式內使用可不再加以說明。 例如:

int a,b; void f1() { …… } float x,y; int fz() { …… } main() { …… }   從上例可以看出a、b、x、y 都是在函式外部定義的外部變數,都是全域性變數。但x,y 定義在函式f1之後,而在f1內又無對x,y的說明,所以它們在f1內無效。 a,b定義在源程式最前面,因此在f1,f2及main內不加說明也可使用。

  [例5.12]輸入正方體的長寬高l,w,h。求體積及三個面x*y,x*z,y*z的面積。

int s1,s2,s3; int vs( int a,int b,int c) { int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } main() { int v,l,w,h; printf(“\ninput length,width and height\n”); scanf(“%d%d%d”,&l,&w,&h); v=vs(l,w,h); printf(“v=%d s1=%d s2=%d s3=%d\n”,v,s1,s2,s3); }   本程式中定義了三個外部變數s1,s2,s3, 用來存放三個面積,其作用域為整個程式。函式vs用來求正方體體積和三個面積, 函式的返回值為體積v。由主函式完成長寬高的輸入及結果輸出。由於C語言規定函式返回值只有一個, 當需要增加函式的返回資料時,用外部變數是一種很好的方式。本例中,如不使用外部變數, 在主函式中就不可能取得v,s1,s2,s3四個值。而採用了外部變數, 在函式vs中求得的s1,s2,s3值在main 中仍然有效。因此外部變數是實現函式之間資料通訊的有效手段。對於全域性變數還有以下幾點說明:

  1. 對於區域性變數的定義和說明,可以不加區分。而對於外部變數則不然,外部變數的定義和外部變數的說明並不是一回事。外部變數定義必須在所有的函式之外,且只能定義一次。其一般形式為: [extern] 型別說明符 變數名,變數名… 其中方括號內的extern可以省去不寫。

  例如: int a,b;

  等效於:

  extern int a,b;     而外部變數說明出現在要使用該外部變數的各個函式內, 在整個程式內,可能出現多次,外部變數說明的一般形式為: extern 型別說明符 變數名,變數名,…; 外部變數在定義時就已分配了記憶體單元, 外部變數定義可作初始賦值,外部變數說明不能再賦初始值, 只是表明在函式內要使用某外部變數。

  2. 外部變數可加強函式模組之間的資料聯絡, 但是又使函式要依賴這些變數,因而使得函式的獨立性降低。從模組化程式設計的觀點來看這是不利的, 因此在不必要時儘量不要使用全域性變數。

  3. 在同一原始檔中,允許全域性變數和區域性變數同名。在區域性變數的作用域內,全域性變數不起作用。

  [例5.13]

int vs(int l,int w) { extern int h; int v; v=l*w*h; return v; } main() { extern int w,h; int l=5; printf(“v=%d”,vs(l,w)); } int l=3,w=4,h=5;   本例程式中,外部變數在最後定義, 因此在前面函式中對要用的外部變數必須進行說明。外部變數l,w和vs函式的形參l,w同名。外部變數都作了初始賦值,mian函式中也對l作了初始化賦值。執行程式時,在printf語句中呼叫vs函式,實參l的值應為main中定義的l值,等於5,外部變數l在main內不起作用;實參w的值為外部變數w的值為4,進入vs後這兩個值傳送給形參l,wvs函式中使用的h 為外部變數,其值為5,因此v的計算結果為100,返回主函式後輸出。變數的儲存型別各種變數的作用域不同, 就其本質來說是因變數的儲存型別相同。所謂儲存型別是指變數佔用記憶體空間的方式, 也稱為儲存方式。

  變數的儲存方式可分為“靜態儲存”和“動態儲存”兩種。

  靜態儲存變數通常是在變數定義時就分定儲存單元並一直保持不變, 直至整個程式結束。5.5.1節中介紹的全域性變數即屬於此類儲存方式。動態儲存變數是在程式執行過程中,使用它時才分配儲存單元, 使用完畢立即釋放。 典型的例子是函式的形式引數,在函式定義時並不給形參分配儲存單元,只是在函式被呼叫時,才予以分配, 呼叫函式完畢立即釋放。如果一個函式被多次呼叫,則反覆地分配、 釋放形參變數的儲存單元。從以上分析可知, 靜態儲存變數是一直存在的, 而動態儲存變數則時而存在時而消失。我們又把這種由於變數儲存方式不同而產生的特性稱變數的生存期。 生存期表示了變數存在的時間。 生存期和作用域是從時間和空間這兩個不同的角度來描述變數的特性,這兩者既有聯絡,又有區別。 一個變數究竟屬於哪一種儲存方式, 並不能僅從其作用域來判斷,還應有明確的儲存型別說明。

  在C語言中,對變數的儲存型別說明有以下四種:

  auto     自動變數   register   暫存器變數   extern    外部變數   static    靜態變數

  自動變數和暫存器變數屬於動態儲存方式, 外部變數和靜態變數屬於靜態儲存方式。在介紹了變數的儲存型別之後, 可以知道對一個變數的說明不僅應說明其資料型別,還應說明其儲存型別。 因此變數說明的完整形式應為: 儲存型別說明符 資料型別說明符 變數名,變數名…; 例如:

  static int a,b;           說明a,b為靜態型別變數   auto char c1,c2;          說明c1,c2為自動字元變數   static int a[5]={1,2,3,4,5};    說明a為靜整型陣列   extern int x,y;           說明x,y為外部整型變數。 總結與區別: 變數根據定義的位置的不同的生命週期,具有不同的作用域,作用域可分為6種:全域性作用域,區域性作用域,語句作用域,類作用域,名稱空間作用域和檔案作用域。

從作用域看:

全域性變數具有全域性作用域。全域性變數只需在一個原始檔中定義,就可以作用於所有的原始檔。當然,其他不包含全域性變數定義的原始檔需要用extern 關鍵字再次宣告這個全域性變數。

靜態區域性變數具有區域性作用域,它只被初始化一次,自從第一次被初始化直到程式執行結束都一直存在,它和全域性變數的區別在於全域性變數對所有的函式都是可見的,而靜態區域性變數只對定義自己的函式體始終可見。

區域性變數也只有區域性作用域,它是自動物件(auto),它在程式執行期間不是一直存在,而是隻在函式執行期間存在,函式的一次呼叫執行結束後,變數被撤銷,其所佔用的記憶體也被收回。

靜態全域性變數也具有全域性作用域,它與全域性變數的區別在於如果程式包含多個檔案的話,它作用於定義它的檔案裡,不能作用到其它檔案裡,即被static關鍵字修飾過的變數具有檔案作用域。這樣即使兩個不同的原始檔都定義了相同名字的靜態全域性變數,它們也是不同的變數。

從分配記憶體空間看: 全域性變數,靜態區域性變數,靜態全域性變數都在靜態儲存區分配空間,而區域性變數在棧裡分配空間。

全域性變數本身就是靜態儲存方式,靜態全域性變數當然也是靜態儲存方式。這兩者在儲存方式上並無不同。這兩者的區別雖在於非靜態全域性變數的作用域是整個源程式,當一個源程式由多個原始檔組成時,非靜態的全域性變數在各個原始檔中都是有效的。而靜態全域性變數則限制了其作用域,即只在定義該變數的原始檔內有效,在同一源程式的其它原始檔中不能使用它。由於靜態全域性變數的作用域侷限於一個原始檔內,只能為該原始檔內的函式公用,因此可以避免在其它原始檔中引起錯誤。

1)、靜態變數會被放在程式的靜態資料儲存區(全域性可見)中,這樣可以在下一次呼叫的時候還可以保持原來的賦值。這一點是它與堆疊變數和堆變數的區別。 2)、變數用static告知編譯器,自己僅僅在變數的作用範圍內可見。這一點是它與全域性變數的區別。

從以上分析可以看出,把區域性變數改變為靜態變數後是改變了它的儲存方式即改變了它的生存期。把全域性變數改變為靜態變數後是改變了它的作用域,限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。應予以注意。

Tips:   A.若全域性變數僅在單個C檔案中訪問,則可以將這個變數修改為靜態全域性變數,以降低模組間的耦合度;   B.若全域性變數僅由單個函式訪問,則可以將這個變數改為該函式的靜態區域性變數,以降低模組間的耦合度;   C.設計和使用訪問動態全域性變數、靜態全域性變數、靜態區域性變數的函式時,需要考慮重入問題,因為他們都放在靜態資料儲存區,全域性可見; D.如果我們需要一個可重入的函式,那麼,我們一定要避免函式中使用static變數(這樣的函式被稱為:帶“內部儲存器”功能的的函式) E.函式中必須要使用static變數情況:比如當某函式的返回值為指標型別時,則必須是static的區域性變數的地址作為返回值,若為auto型別,則返回為錯指標。