1. 程式人生 > >c語言全域性變數和區域性變數問題彙總(轉)

c語言全域性變數和區域性變數問題彙總(轉)

1、區域性變數能否和全域性變數重名?

答:能,區域性會遮蔽全域性。要用全域性變數,需要使用"::"

區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。對於有些編譯器而言,在同一個函式內可以定義多個同名的區域性變數,比如在兩個迴圈體內都定義一個同名的區域性變數,而那個區域性變數的作用域就在那個迴圈體內。

2、如何引用一個已經定義過的全域性變數?

答:extern

可以用引用標頭檔案的方式,也可以用extern關鍵字,如果用引用標頭檔案方式來引用某個在標頭檔案中宣告的全域性變理,假定你將那個變寫錯了,那麼在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那麼在編譯期間不會報錯,而在連線期間報錯。

3、全域性變數可不可以定義在可被多個.C檔案包含的標頭檔案中?為什麼?

答:可以,在不同的C檔案中以static形式來宣告同名全域性變數。

可以在不同的C檔案中宣告同名的全域性變數,前提是其中只能有一個C檔案中對此變數賦初值,此時連線不會出錯

4、static全域性變數與普通的全域性變數有什麼區別?static區域性變數和普通區域性變數有什麼區別?static函式與普通函式有什麼區別?

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

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

static函式與普通函式作用域不同,僅在本檔案。只在當前原始檔中使用的函式應該說明為內部函式(static),內部函式應該在當前原始檔中說明和定義。對於可在當前原始檔以外使用的函式,應該在一個頭檔案中說明,要使用這些函式的原始檔要包含這個標頭檔案

static全域性變數與普通的全域性變數有什麼區別:static全域性變數只初使化一次,防止在其他檔案單元中被引用;

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

static函式與普通函式有什麼區別:static函式在記憶體中只有一份,普通函式在每個被呼叫中維持一份拷貝.

5、程式的區域性變數存在於(堆疊)中,全域性變數存在於(靜態區 )中,動態申請資料存在於( 堆)中。

      變數可以在程式中三個地方說明: 函式內部、函式的引數定義中或所有的函式外部。根據所定義位置的不同, 變數可分為區域性變數、形式引數和全程變數。從空間角度來看,變數可以分為全域性變數和區域性變數,而從時間角度來分的 可以有靜態儲存變數和動態儲存變數之分。

一。全域性變數和區域性變數

1。區域性變數

他是 指在函式內部定義的變數 作用域為定義區域性變數的函式 也就是說他只能在定義他的函式中使用

最值得注意的是 只有在程式執行到定義他的模組時才能生成,一旦執行退出該模組則起變數消失

eg.

func ()

{
   int x;   區域性變數x的作用域 很明確
   ......
}


2。全域性變數

在程式執行的過程中一直有效

eg.

int x=1;

func ()

{
   x=x+1;

}

func1 ()

{
  x=x-1;

}

main ()

{

}
由此不難看出整型x的作用範圍


對於全域性變數 如果在定義的時候不做初始化 則系統將自動為起賦值 數值型為0

字元型為空'/0'

全域性變數的弊端 增加記憶體開銷 降低函式的通用性

定義全域性變數時 理想的位置是在檔案的開頭 當這些函式以及同一個程式中的其他源程式檔案中的某些函式需要使用該全域性變數時 在函式內部對該變數使用extern 加以說明 說明他是外部的 

(這裡還要做詳細的講解)

eg.

main ()

{
extern int a,b;
printf ("mIn is %d/n",min(a,b));
}

int a=1,b=2;
int min (x,y)
int x,y;
{
int z;
z=x<y?x:y;
return(z);
}
我還要說明的是 對外部變數的說明和對全域性變數的定義不是一回事

對外部變數的說明 只是宣告該變數是在外部定義過的一個全域性變數 在這裡引用 而對全域性變數的定義則是要對起分配儲存單元的

一個全域性變數只能定義一次 可是卻可以多次引用

*** 在同一原始檔中,全域性變數和區域性變數同名時,在區域性變數的作用域內,全域性變數不起作用的。

二。靜態儲存變數和動態儲存變數

對於程式執行期間根據需要進行臨時動態分配儲存空間的變數 為動態儲存變數

對於那些程式執行期間永久佔用固定記憶體的變數 稱為靜態儲存變數


還要說明的是 程式的指令程式碼是存放在程式程式碼區的 靜態儲存變數是存放在靜態資料區的 包括全域性變數等 而程式中的動態儲存變數存放在動態資料區 如函式的形參以及函式呼叫時的返回地址等


三。C語言中的變數儲存分類指定
    
    auto 
    auto稱為自動變數 如果函式不做其他說明的話 均為自動變數


    static 
    static稱為靜態變數。根據變數的型別可以分為靜態區域性變數和靜態全程變數。 
    1. 靜態區域性變數 
    

     它與區域性變數的區別在於: 在函式退出時, 這個變數始終存在, 但不能被其它函式使用, 當再次進入該函式時, 將儲存上次的結果。其它與區域性變數一樣。 
    

    2. 靜態全程變數 
    

     Turbo C2.0允許將大型程式分成若干獨立模組檔案分別編譯, 然後將所有模組的目標檔案連線在一起, 從而提高編譯速度, 同時也便於軟體的管理和維護。靜態全程變數就是指只在定義它的原始檔中可見而在其它原始檔中不可見的變數。它與全程變數的區別是: 全程變數可以再說明為外部變數(extern), 被其它原始檔使用, 而靜態全程變數卻不能再被說明為外部的, 即只能被所在的原始檔使用。 
   
   
    extern 
    extern稱為外部變數。為了使變數除了在定義它的原始檔中可以使用外, 還要被其它檔案使用。因此,   必須將全程變數通知每一個程式模組檔案,   此時可用extern來說明。 


eg. 
        檔案1為file1.c                  檔案2為file2.c 
    int i, j;/*定義全程變數*/        extern int i, j;/*說明將i, j從 
                                                     檔案1中複製過來*/ 
    char c;                          extern char c; /*將c複製過來*/ 
    void func1(int k);               func2()        /*使用者定義函式*/ 
                                     { 
    main()                              static float k;/*定義靜態變數*/ 
    {                                   i=j*5/100; 
          func1(20);/*呼叫函式*/        k=i/1.5; 
          func2();                           . 
          .                                  . 
          .                                  . 
          .                            } 
     } 
     func1(int k) /*使用者定義函式*/ 
     { 
          j=k*100; 
     } 

    對於以上兩個檔案file1.c和file2.c, 用Turbo C2.0的整合開發環境進行編譯 
連線時, 首先應建立一個.prj的檔案。例如file.prj, 該檔案內容如下: 
     file1.c 
     file2.c 
    然後將file.prj的檔名寫入主選單Project中的Project Name項中。 再用F9 
編譯連線, 就可產生一個檔名為fioe.exe的可執行檔案。 
     
  

    register 
    register稱為暫存器變數。它只能用於整型和字元型變數。定義符register說明的變數被Turbo C2.0儲存在CPU的暫存器中,  而不是象普通的變數那樣儲存在記憶體中, 這樣可以提高運算速度。但是Turbo C2.0只允許同時定義兩個暫存器變數,一旦超過兩個, 編譯程式會自動地將超過限制數目的暫存器變數當作非暫存器變數來處理。因此, 暫存器變數常用在同一變數名頻繁出現的地方。 
    另外, 暫存器變數只適用於區域性變數和函式的形式引數, 它屬於auto型變數, 
因此, 不能用作全程變數。定義一個整型暫存器變數可寫成: 
      register int a; 
    對於以上所介紹的變數型別和變數儲存型別將會在以後的學習中, 通過例行程式中的定義、使用來逐漸加深理解。 1.程式的記憶體區域   並不是所有的變數時時刻刻都是可知的。一些變數在整個程式中都是可見的,它們稱為全域性變數。一些變數只能在一個函式中可知,稱為區域性變數。要了解變數的這些屬性,應先弄清程式在記憶體中的分佈區域,見圖5-2。 圖5-2 程式在記憶體中的區域   一個程式將作業系統分配給其執行的記憶體塊分為4個區域:
  (1)程式碼區,存放程式的程式碼,即程式中的各個函式程式碼塊。
  (2)全域性資料區,存放程式的全域性資料和靜態資料。
  (3)堆區,存放程式的動態資料。
  (4)棧區,存放程式的區域性資料,即各個函式中的資料。 2.全域性變數   在函式外邊訪問的變數被認為是全域性變數,並在程式的每個函式中是可見的。全域性變數存放在記憶體的全域性資料區。全域性變數由編譯器建立,並且初始化為0,在定義全域性變數時,進行專門初始化的除外。
  例如,下面的程式碼定義並使用了全域性變數。:
    int n=5; //全域性變數
    void main()
    {
     int m=n;
     //...
    }
    void func()
    {
     int s;
     n=s;
     //...
    }
  n在任何函式的外部定義。 n被初始化為5, 如果n不在定義時初始化,則C++將其初始化為0。 main()函式使用變數n1,函式func()修改變數n。兩個函式都訪問了同一個記憶體區域。這樣定義的全域性變數。在所有函式中都可見。如果一個函式修改了n,則所有其他的函式都會看到修改後的變數。
  全域性變數在主函式main()執行之前就開始存在了。所以主函式中可以訪問n變數。 全域性變數通常在程式頂部定義。全域性變數一旦定義後就在程式的任何地方可知。可以在程式中間的任何地方定義全域性變數,但要在任何函式之外。全域性變數定義之前的所有函式定義,不會知道該變數。例如:
    void main()
    {
     int m=n; //error:n無定義
     //...
    }
    int n;//全域性變數
    void func()
    {
     int s=3;
     n=s;
     //...
    }
  該程式碼中的全域性變數n不能被主函式main()訪問。 編譯該程式碼,將會引起main()中的m初始化語句報告一個“n無定義”的錯誤。 3.區域性變數   在函式內部定義的變數僅在該函式內是可見的。另外,區域性變數的型別修飾是auto,表示該變數在棧中分配空間,但習慣上都省略auto。例如:
    void main()
    {
     int n; //等價於auto intn;
     //...
    }
    void func()
    {
     int n;
     //...
    }
  程式碼中兩個函式都包含一個變數定義語句。在函式內定義的變數區域性於該函式。main()函式中有一個變數n,func()函式中也有一個變數n,但它們是兩個不同位置的變數。
  一個函式可以為區域性變數定義任何名字,而不用擔心其他函式使用過同樣的名字。 這個特點和區域性變數的存在性使C++適合於由多個程式設計師共同參與的程式設計專案。專案管理員為程式設計師指定編寫函式的任務,併為程式提供引數和期望的返回值。然後,程式設計師著手編寫函式,而不用瞭解程式的其他部分和專案中其他程式設計師所使用的變數名。
  函式中的區域性變數存放在棧區。在函式開始執行時,區域性變數在棧區被分配空間,函式退出時,區域性變數隨之消失。
  區域性變數沒有初始化。如果區域性變數不被顯式初始化,那麼,其內容是不可預料的。例如:
    //*********************
    //**   ch5_1.cpp  **
    //*********************     #include <iostream.h>     int func1();
    int func2();     void main()
    {
     func1();
     cout <<func2() <<endl;
    }     int func1()
    {
     int n=12345;
     return n;
    }     int func2()
    {
     int m;
     return m; //warning:possible use of 'm' before definition
    }   執行結果為:
    12345
  主函式main()先後呼叫了函式func1()和func2(),它們都是無參並返回整數的函式。
  在func1()中,定義了局部變數n,並給其初始化為12345。在func2()中,定義了局部變數m,沒有初始化。 可是在將該變數值返回後,在主函式中輸出該值,卻發現為12345, 恰好就是funcl()函式中初始化的值。這說明,func2()中,沒有顯式初始化的區域性變數m,C++也未給其預設初始化,其值保留為原記憶體位置的值。那麼,原記憶體位置為什麼恰巧是存放值12345的位置呢?請見下節“函式呼叫機制”。 要點: 1、全域性變數: (1)在函式外定義
(2)在全域性可見
(3)一般在程式頂部定義
(4)存放在記憶體的全域性資料區
(5)如在定義時未初始化,則其值為0
(6)如果一個函式修改了n,則所有其他的函式都會看到修改後的變數 2、區域性變數: (1)在函式內部定義
(2)僅在該函式內可見
(3)存放於棧區,函式退出時,區域性變數隨之消失
(4)不同函式可使用同一個變數名
(5)如果區域性變數不被顯式初始化,其內容則不可預料

區域性變數 

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

區域性變數從儲存方式上可分為動態(auto)儲存型別和靜態(static)儲存型別。

動態儲存型別的區域性變數都是動態的分配儲存空間,資料儲存在動態儲存區(棧)中。函式呼叫結束後自動釋放,生存期是在宣告該變數的函式執行過程。

靜態儲存型別的區域性變數則是靜態的分配儲存空間,資料儲存在靜態儲存區中。在程式整個執行期間都不釋放,生存期貫穿於程式執行的整個過程。

函式中的區域性變數,如不專門宣告為static儲存類別,預設都是動態地分配儲存空間的,我們在平時的宣告變數的過程中auto都是預設省略的。

全域性變數

全域性變數也稱為外部變數,是在函式的外部定義的,它的作用域為從變數定義處開始,到本程式檔案的末尾。全域性變數全部存放在靜態儲存區,在程式開始執行時給全域性變數分配儲存區,程式行完畢就釋放。在程式執行過程中它們佔據固定的儲存單元,而不動態地進行分配和釋放;

如果外部變數不在檔案的開頭定義,其有效作用域只限於定義處到檔案終

如果在定義點之前的函式想引用該外部變數,則應該在引用之前用關鍵字extern對該變數作“外部變數宣告”。表示該變數是一個已經定義的外部變數。有了此宣告,就可以從“宣告”處起,合法地使用該外部變數。其有效作用域就被拓展到從這個檔案extern宣告處到檔案結束

如果在全域性變數宣告的時候,前面加上關鍵字static,那麼其他檔案就不能再訪問和使用該變數,其有效作用域只限於定義處到檔案終

區域性變數能否和全域性變數重名

       區域性變數能和全域性變數重名,但是區域性變數會遮蔽全域性變數。在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。

PS:這對extern宣告的全域性變數也一樣

--------------------------------------------------------------- 一般全域性變數存放在資料區,區域性變數存放在棧區, 
動態變數存放在堆區,函式程式碼放在程式碼區。 
--------------------------------------------------------------- 

棧區是普通的棧資料結構,遵循LIFO後進先出的規則,區域性變數安排在那裡是ASM時就規定的,這樣可以在一個函式結束後平衡堆疊,操作簡單,效率高 
堆(動態區)在這裡應當叫堆疊(不要和資料結構中的堆搞混)是程式在編譯時產生的一塊用於產生動態記憶體分配使用的塊,操作比較棧要麻煩許多,在分配時要判斷最優的地址(防止產生無用的記憶體碎片(由於屢次的NEW和DELETE產生的夾在兩塊使用中記憶體中的空餘小記憶體(不容易被分配))),分配和回收時的效率比棧低多了 
--------------------------------------------------------------- 

棧是系統提供的功能,特點是快速高效,缺點是有限制,資料不靈活;而棧是函式庫提供的功能,特點是靈活方便,資料適應面廣泛,但是效率 >有一定降低。棧是系統資料結構,對於程序/執行緒是唯一的;堆是函式庫內部資料結構,不一定唯一。不同堆分配的記憶體無法互相操作。棧空間分靜態分配和動態分配兩種。靜態分配是編譯器完成的,比如自動變數(auto)的分配。動態分配由alloca函式完成。棧的動態分配無需釋放(是自動的),也就沒有釋放函式。為可移植>的程式起見,棧的動態分配操作是不被鼓勵的!堆空間的分配總是動態的,雖然程式結束時所有的資料空間都會被釋放回系統,但是精確的申請記憶體/ 釋放記憶體匹>配是良好程式的基本要素。 
這是我對堆與棧收藏內容~ 
--------------------------------------------------------------- 

堆是程式設計師管理的,棧是系統管理的. --------------------------------------------------------------- --------------------------------------------------------------- --------------------------------------------------------------- 另外關於靜態和全域性的一些問題: 靜態變數的特點:

1、  一次儲存:靜態區域性變數只被初始化一次,下一次初始化根據上一次的結果值,有點類似於c++中類的靜態成員變數,即無論該型別生成多少個例項物件,所有的物件共用一個靜態變數,到這裡就是無論這個函式呼叫多少次,該靜態變數只初始化一次,並沒有因為超出其生存期而被銷燬,只是外部不可見而已,用個例子說明之:

void  fun1( int  v )
{
    static  int  value = v;
    static  int  value = v;
}
int  main( int  arc, char *args[ ])
{
    fun1( 50 );
    fun1( 100 );
}

        執行的結果是:value : 50  value : 50
        說明在第二次呼叫fun1( )時的初始化value的採用的是上一次value的值,value在靜態區的儲存空間並沒有因為fun1( )的結束而被釋放,即體現了一次儲存;

2、  作用域限定:靜態修飾的作用域限定功能同時體現在函式與變數上;

a)         對於函式而言,任何用static修飾的函式,其作用域僅為當前原始檔,而對外部來說這個函式是不可見的,即只有和其在同一原始檔中的函式才能呼叫這個靜態函式;反過來說,如果一個函式僅僅被同一原始檔中的其他函式呼叫,那麼這個函式應該宣告為靜態的,這樣做的好處在於:可以一定程度上的解決不同原始檔之間函式的命名衝突問題;

b)        對於變數而言,static修飾的全域性變數,只在當前原始檔中有效,對外部不可見,外部檔案不能夠引用;

顧名思義,全域性變數是指能夠在全域性引用的變數,相對於區域性變數的概念,也叫外部變數;同靜態變數一樣,全域性變數位於靜態資料區,全域性變數一處定義,多處引用,用關鍵字“extern”引用“外部”的變數。

全域性變數也可以是靜態的,在前面有過說明,靜態全域性變數的意義就是不讓“外部”引用,是單個原始檔裡的全域性變數,即是編譯階段的全域性變數,而不是連線階段的全域性變數。

通過上面的分析,我們不難得出以下結論:

1、  靜態函式普通函式的區別在於:靜態函式不可以被同一原始檔以外的函式呼叫。

2、  靜態區域性變數普通區域性變數的區別在於:靜態區域性變數只初始化一次,下一次初始化實際上是依然是上一次的變數;

3、  靜態全域性變數普通全域性變數的區別在於:靜態全域性變數的作用域僅限於所在的原始檔。

---------------------------------------------------------------

---------------------------------------------------------------

---------------------------------------------------------------

靜態變數的型別說明符是static。 靜態變數當然是屬於靜態儲存方式,但是屬於靜態儲存方式的量不一定就是靜態變數,例如外部變數雖屬於靜態儲存方式,但不一定是靜態變數,必須由 static加以定義後才能成為靜態外部變數,或稱靜態全域性變數。對於自動變數,它屬於動態儲存方式。 但是也可以用static定義它為靜態自動變數,或稱靜態區域性變數,從而成為靜態儲存方式。

  由此看來, 一個變數可由static進行再說明,並改變其原有的儲存方式。

  1. 靜態區域性變數

  在區域性變數的說明前再加上static說明符就構成靜態區域性變數。

  例如:

  static int a,b;

  static float array[5]={1,2,3,4,5};

  靜態區域性變數屬於靜態儲存方式,它具有以下特點:

  (1)靜態區域性變數在函式內定義,但不象自動變數那樣,當呼叫時就存在,退出函式時就消失。靜態區域性變數始終存在著,也就是說它的生存期為整個源程式。

  (2)靜態區域性變數的生存期雖然為整個源程式,但是其作用域仍與自動變數相同,即只能在定義該變數的函式內使用該變數。退出該函式後,儘管該變數還繼續存在,但不能使用它。

  (3)允許對構造類靜態區域性量賦初值。若未賦以初值,則由系統自動賦以0值。

  (4)對基本型別的靜態區域性變數若在說明時未賦以初值,則系統自動賦予0值。而對自動變數不賦初值,則其值是不定的。 根據靜態區域性變數的特點, 可以看出它是一種生存期為整個源程式的量。雖然離開定義它的函式後不能使用,但如再次呼叫定義它的函式時,它又可繼續使用,而且儲存了前次被呼叫後留下的值。 因此,當多次呼叫一個函式且要求在呼叫之間保留某些變數的值時,可考慮採用靜態區域性變數。雖然用全域性變數也可以達到上述目的,但全域性變數有時會造成意外的副作用,因此仍以採用區域性靜態變數為宜

  2.靜態全域性變數

  全域性變數(外部變數)的說明之前再冠以static 就構成了靜態的全域性變數。全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。 這兩者在儲存方式上並無不同。這兩者的區別雖在於非靜態全域性變數的作用域是整個源程式,當一個源程式由多個原始檔組成時,非靜態的全域性變數在各個原始檔中都是有效的。 而靜態全域性變數則限制了其作用域, 即只在定義該變數的原始檔內有效, 在同一源程式的其它原始檔中不能使用它。由於靜態全域性變數的作用域侷限於一個原始檔內,只能為該原始檔內的函式公用,因此可以避免在其它原始檔中引起錯誤。從以上分析可以看出, 把區域性變數改變為靜態變數後是改變了它的儲存方式即改變了它的生存期。把全域性變數改變為靜態變數後是改變了它的作用域,限制了它的使用範圍。因此static 這個說明符在不同的地方所起的作用是不同的。應予以注意。

  靜態變數

  除範圍之外,變數還有存活期,在這一期間變數能夠保持它們的值。在應用程式的存活期內一直保持模組級變數和公用變數的值。但是,對於 Dim 宣告的區域性變數以及宣告區域性變數的過程,僅當過程在執行時這些區域性變數才存在。通常,當一個過程執行完畢,它的區域性變數的值就已經不存在,而且變數所佔據的記憶體也被釋放。當下一次執行該過程時,它的所有區域性變數將重新初始化。

  但可將區域性變數定義成靜態的,從而保留變數的值。在過程內部用 Static 關鍵字宣告一個或多個變數,其用法和 Dim 語句完全一樣:

  Static Depth

  例如,下面的函式將儲存在靜態變數 Accumulate 中的以前的運營總值與一個新值相加,以計算運營總值。

  Function RunningTotal (num)

  Static ApplesSold

  ApplesSold = ApplesSold + num

  RunningTotal = ApplesSold

  End Function

  如果用 Dim 而不用 Static 宣告 ApplesSold,則以前的累計值不會通過呼叫函式保留下來,函式只會簡單地返回呼叫它的那個相同值。

  在模組的宣告段宣告 ApplesSold,並使它成為模組級變數,由此也會收到同樣效果。但是,這種方法一旦改變變數的範圍,過程就不再對變數排他性存取。由於其它過程也可以訪問和改變變數的值,所以運營總值也許不可靠,程式碼將更難於維護。

  宣告所有的區域性變數為靜態變數

  為了使過程中所有的區域性變數為靜態變數,可在過程頭的起始處加上 Static 關鍵字。例如:

  Static Function RunningTotal (num)

  這就使過程中的所有區域性變數都變為靜態,無論它們是用 Static、Dim 或 Private 宣告的還是隱式宣告的。可以將 Static 放在任何 Sub 或 Funtion 過程頭的前面,包括事件過程和宣告為 Private 的過程。