【轉載】static修飾詞

(來源)http://www.csie.nctu.edu.tw/~skyang/static.zhtw.htm

在全域變數或函式的宣告之前加上 static,就讓它們不能跨 .cpp 檔使用,如下例中的 compare_grade( )它的目的僅在於提供給下面的 qsort( ) 使用,所以在它函式宣告之前加了 static,這麼一來它就只能在這個 .cpp 檔案中有效,在其他的 .cpp 檔案中就算以 extern 來援用它也不可以,另一方面,在其他的 .cpp 檔案中就算有同名的 compare_grade( ) 函式也沒關係。
#include <math.h>
#include <stdlib.h>
#include "GradeList.h"

static int compare_grade(const void *ptr1, const void *ptr2)
{
    float f1 = *((float *)ptr1);  // 把 ptr 轉型成 (float *)再取值
    float f2 = *((float *)ptr2);

    if (f1 > f2) { return 1; }    // 如果 f1 比較大就傳回正數
    else if (f1 < f2) {  return -1;  }

    return 0;
}

// 用 compare_grade( ) 比較 grade 中每個 float,由小到大排序:
void GradeList::sort_grade_list(void)
{   qsort(grade, students, sizeof(float), compare_grade);  }
另一個與這樣的 static 相對的修飾詞是 extern,這個修飾詞用來讓你使用其他檔案的全域變數與函式,在變數宣告或函式原型宣告前加上 extern,則編譯器不會產生新變數或函式,而是在連結(linking)階段才使用其他檔案中的變數或函式,例如:
A.cpp
int curUsers = 5;

B.cpp
extern int curUsers; // 不可以再給初始值
修飾函式中的區域變數
以下這個範例是 StaticVariables.cpp,使用 static 來修飾區域變數很簡單地就是在區域變數的宣告之前加上 static,如下例右邊所示,這個例子的 func( ) 函式會把整數變數 x 以 printf( ) 顯示後加一,然後在 main( ) 當中呼叫了 func( ) 函式 5 次。在左邊的例子裡,區域變數 x 未用 static 修飾,所以它的行為就是典型的區域變數一般,每一次呼叫 func( ) 都會重新產生一個區域變數 x 並初始化為 1,所以在執行結果當中,呼叫了 5 次 func( ) 都是顯示 x = 1,而在右邊的例子裡,由於 x 用了 static 去修飾它,所以它會保留上一次執行 func( ) 實的原值,而 func( ) 的內容是顯示後加一,所以 main( ) 中呼叫了 func( ) 函式 5 次的執行結果是 x 由 1 遞增到 5 了。
未使用static 修飾詞 使用 static 修飾區域變數 x
#include <stdio.h>

void func(void)
{
    int x = 1;
    printf("x = %d\n", x);
    ++x;
}

void main(void)
{
    for (int i=0; i<5; ++i)
    {  func( );  }
}
#include <stdio.h>

void func(void)
{
    static int x = 1; // 只會初始化一次
    printf("x = %d\n", x);
    ++x;
}

void main(void)
{
    for (int i=0; i<5; ++i)
    {  func( );  }
}
執行結果 執行結果
x = 1
x = 1
x = 1
x = 1
x = 1
x = 1
x = 2
x = 3
x = 4
x = 5
在函式當中,我們經常會需要用到上一次執行的結果,如果不使用 static,就必須使用全域變數或是增加類別中的成員來紀錄它們,當然我們也可以這麼做,但會使得程式中冒出許多莫名其妙的計數器之類用途的全域變數或類別成員,這樣的程式碼看起來就不太清爽了。
修飾類別中的資料或函式成員
前篇已經提到 class 的前身是 struct,故以資料成員的原理和使用機制來說都和 struct 差不多,在類別中的一個資料成員也是以此類別宣告出的變數(稱為 instance)之記憶體布局(memory layout)中的一欄,而函式成員也必須由 instance 透過 . 運算子或 -> 運算子使用,才能夠在函式當中使用類別的資料成員,換言之,未以 static 修飾的變數或函式成員,必須有實體(instance)才能被使用,例如:
GradeList list;
list.adjust_grade_list();
而類別中以 static 修飾的變數或函式成員,則不受這個限制,變成像是命名空間中的全域變數與函式呼叫,例如:
GradeList::get_sqrt_grade(x);
但也因為如此,static 函式成員中不可以使用任何非 static 變數成員,這是因為沒有 instance,那些成員當然不存在,所以,在類別中以 static 修飾的資料與函式成員,他們的性質和非 static 成員截然不同,前面說到 class 宣告會自成命名空間,他們是此命名空間中的全域變數與函數。除此之外要注意的是 static 變數成員必須另外在類別外再做一次變數宣告,這是和 struct 與命名空間都不一樣的地方,請見下面經過節錄的 Grade Class 2 例子:
GradeList.h
class GradeList
{
    public:
    char teacher[16];        // 教師的名稱
    GRADE * grade;           // 以動態陣列儲存
    int students;            // grade 陣列中的學生數

    static char school[16]; // 學校名稱

    private:
    int size;                // grade 目前已配置的空間數量

    public:
    GradeList();             // 建構函式: 初始化grade, students, size
    ~GradeList();            // 解構函式: 釋放grade 所佔用的記憶體

    // 增加分數:
    void add_grade(float value, int id = -1);

    // 只是提供一個計算公式:
    static float get_sqrt_grade(float x);
};

留言

這個網誌中的熱門文章

為 Line-in 設定音量

Firefox: 設定滑鼠滾輪捲動行數