變數的作用範圍(scope)(此a非彼a)
更新日期:2022.02.10
 
撰寫:曾老師
 
    在前面課程,談的變數主要在主程式main()的大刮號 {...} 裡來宣告變數,
    學到函式這邊的程式結構後,或許有同學會發現,
    變數會因為在宣告、和使用的位置上,
    會有所差別...

    這其中有幾種狀態,說明如下:

  • 全域變數(global variable):
    宣告位置在程式(函式、主程式main)以外的變數
    此類的變數在所有函式、主程式main中,都能存取使用,
    變數的作用範圍(scope)、或稱生命週期、或視域,
    是從執行程式開始,到程式結束才終止。


    範例1a:全域變數
    #include <stdio.h>
    #include <stdlib.h>
    int a; //宣告全域變數a

    void func() {
        a=7;
    }

    main()
    {
        int b=2, c=3;
        a = b+c;
        func();
        printf("a=%d\n", a);

        system("PAUSE");
        return 0;
    }
    請執行上述程式,並記錄結果。

    說明:變數a,宣告於函式func、main的{ }外部,在main中經運算後a為5,
       但之後呼叫func,在func更改a為7;
       由於是全域變數,所以a變數的更改,都可以保持紀錄。


    範例1b(此a非彼a):
    #include <stdio.h>
    #include <stdlib.h>
    int a; //宣告全域變數a

    void func() {
        a=7;
    }

    main()
    {
        int a;
        int b=2, c=3;
        a = b+c;
        func();
        printf("a=%d\n", a);

        system("PAUSE");
        return 0;
    }
    如果在主程式main中,也宣告一個變數a,請執行並觀察結果會如何?

    說明:在main()裡,會以宣告的a變數作為main的a變數,
       而不是用main外面宣告的a變數;
       簡單來看,如果區域{...}內。有宣告的同名變數,
       會優先以範圍小的變數來使用。



  • 區域變數(local variable):
    在函式/副程式中宣告的變數,或在參數列宣告的變數;
    該類的變數,只能在該函式內能存取使用,
    無法讓其他函式、或主程式main()內來存取使用。

    範例2a:區域變數 (函式內的a)func_var02a.c
    #include <stdio.h>
    #include <stdlib.h>
    void func() {
        int a;
        a=1;
    }
    main()
    {
        int a=5;
        func();
        printf("main a=%d\n", a);
       
        system("PAUSE");
        return 0;
    }
    請執行上述程式,並記錄結果。
    說明:在main裡的 a變數,初始設定為5,之後呼叫func(),
       因函式func裡也有宣告a變數,此a為區域變數,一旦離開函式則作用範圍消失;
       所以,當main繼續執行下去,其中的a變數,則仍是前面的a (非函式中的a)。




    範例2b: 靜態變數func_var02b.c
    #include <stdio.h>
    #include <stdlib.h>
    void counter()
    {
        static int a=1;
        printf("counter:a=%d\n", a);
        a=a+1;
    }

    main()
    {
        //a=10;
        counter();
        counter();

        system("PAUSE");
        return 0;
    }
    a.執行上述程式,看看執行結果。
    b.去掉static,重編執行,比較結果為何?




    static 修飾詞是靜態的意思,
    作用是讓宣告的變數生命週期,在該檔案的程式執行結束前,都能存在。
    只能在該函式中修改、改變此變數的值
    (也就是需要透過呼叫該函式執行修改,不能在主程式直接設定改變)。
    上述呼叫兩次counter,所以a值會累積.



    範例2c: 靜態變數func_var03.c
    #include <stdio.h>
    #include <stdlib.h>
    void swap(int x, int y)
    {
        int tmp;
        tmp = x;
        x = y;
        y = tmp;
        //printf("swapfunc: x=%d y=%d\n", x, y);
    }

    main()
    {
        int x=2, y=3;
        swap(x, y);
        printf("x=%d y=%d\n", x, y);

        system("PAUSE");
        return 0;
    }
    a.執行上述程式,看看執行結果。
    b.把swap裡的註解去掉,再看看結果為何?
    c.為什麼會這樣呢?





  • 區塊變數(block variable)(C++):

    此類變數宣告在結構如 for、while等的區塊{ }內,
    當離開該語法大刮號{ }區塊,則會終止不能使用。
    C語言裡,是沒有這種(name space)的定義,
    在一些gcc編譯器下,在for裡宣告變數會出現錯誤;
    但在C++裡是有支援這樣的用法,會以大刮號刮住範圍為一個name space。
    PS: scope範圍小的變數、會暫時取代scope範圍大的變數值
    範例:func_var04.cpp (副檔名請存.cpp)
    #include <stdio.h>
    #include <stdlib.h>
    main()
    {
        int i=10;
        for(int i=0; i<=15; i++) {
           printf("inner i=%d\n", i);
        }
        printf("outer i=%d\n", i);
        system("PAUSE");
        return 0;
    }
    執行上述程式,觀察outer i的結果。





  • 外部變數extern:

    當在檔案中宣告extern變數時,編譯器會在連結的檔案內尋找這名稱的外部變數名,
    如果有,則拿來使用,若無,則會有錯誤訊息!
    檔案: test1.c
    #include <stdio.h>
    #include <stdlib.h>
    main()
    {
        extern int var_a;  //呼叫外部檔案的變數
        printf("var_a=%d\n", var_a);

        system("PAUSE");
        return 0;
    }

    檔案: test2.c
    int var_a=2022;

    注意:若在Dev C下要執行,需以開啟專案(Project)的模式方式(副檔名.dev),
       在開啟新專案後,選擇「Console Application」,
       然後在專案下,以「新增檔案」或「將檔案加入專案」選項,
       將上述兩個檔案加入,以一般執行方式去編譯執行即可。




  • const 唯讀變數:

    const 常數變數,此修飾詞使用在變數上時,會讓此變數在其作用範圍scope內,都不能被更改。
    意思是此變數只能讀取(唯讀)。

    例1:
    const float pi=3.1415;
    說明:宣告定義浮點數 pi=3.1415為靜態常數,且在程式中不能變更pi.


    但在指標變數作用下,不同位置會有不一樣的效果;
    例2: a指標指向一個const整數,意思是不能透過指標a去更改c的值.
    int c=5;
    const int* a=&c;


    例3: a是一個const整數指標, 指向c,無法更改a這個指標變數.
    int c=5;
    int* const a=&c;


    練習 func_var05.c:
    #include <stdio.h>
    #include <stdlib.h>

    void test1() {
       int c=5, d=6;
       const int* a=&c; //讓指標不能改變「指向位址的值」.
       //*a=7; // <-錯誤
       a=&d;   // 但可以改變指標變數的值
       printf("a=%d\n", *a);
    }

    void test2() {
       int c=5, d=6;
       int* const a=&c; //讓指標變數不能改變「目前指向的位址」.
       *a=7;   // 但可改變指標變數指向位址的值.
       //a=&d; // <-錯誤
       printf("a=%d\n", *a);
    }

    main()
    {
       test1();
       test2();

       system("PAUSE");
       return 0;
    }
    執行比較 test1(), test2()的結果。


  • register 變數:
    register此修飾詞,原本是暫存器的意思,
    早期的電腦,其暫存器的處理速度遠比記憶體來的快,
    所以,當初的設計是讓編譯器將有此宣告的變數,放入暫存器內加速執行的處理;
    但隨著編譯技術的優化,及暫存器資源的配置逐漸讓給OS控管,
    這樣的資源會由作業系統端來配置,不一定真的能使用到,逐漸意義式微了。



  • 紙筆算算看:


參考資料: