1. 程式人生 > >static的作用,在面向物件和麵向過程中的應用

static的作用,在面向物件和麵向過程中的應用

 在C語言中,static的字面意思很容易把我們匯入歧途,其實它的作用有三條。

(1)先來介紹它的第一條也是最重要的一條:隱藏。

當我們同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。為理解這句話,我舉例來說明。我們要同時編譯兩個原始檔,一個是a.c,另一個是main.c。

下面是a.c的內容

char a = 'A'; // global variable
void msg()
{
     printf("Hello\n");
}

下面是main.c的內容

int main(void)
{   
    extern char a;    // extern variable must be declared before use
     printf("%c ", a);
     (void)msg();
    return 0;
}

程式的執行結果是:

A Hello

你可能會問:為什麼在a.c中定義的全域性變數a和函式msg能在main.c中使用?前面說過,所有未加static字首的全域性變數和函式都具有全域性可見性,其它的原始檔也能訪問。此例中,a是全域性變數,msg是函式,並且都沒有加static 字首,因此對於另外的原始檔main.c是可見的。

如果加了static,就會對其它原始檔隱藏。例如在a和msg的定義前加上 static,main.c就看不到它們了。利用這一特性可以在不同的檔案中定義同名函式和同名變數,而不必擔心命名衝突。Static可以用作函式和變數的字首,對於函式來講,static的作用僅限於隱藏,而對於變數,static還有下面兩個作用。(2)static的第二個作用是保持變數內容的持久。儲存在靜態資料區的變數會在程式剛開始執行時就完成初始化,也是唯一的一次初始化。共有兩種變數儲存在靜態儲存區:全域性變數和static變數,只不過和全域性變數比起來,static可以控制變數的可見範圍,說到底static還是用來隱藏的。雖然這種用法不常見,但我還是舉一個例子。

#include <stdio.h>
int fun(void){
    static int count = 10;    // 事實上此賦值語句從來沒有執行過
    return count--;
}
int count = 1;
int main(void)
{   
     printf("global\t\tlocal static\n");
    for(; count <= 10; ++count)
         printf("%d\t\t%d\n", count, fun());   
    return 0;
}

程式的執行結果是:

global        local static
1               10
2               9
3               8
4               7
5               6
6               5
7               4
8               3
9               2
10             1

(3)static的第三個作用是預設初始化為0。其實全域性變數也具備這一屬性,因為全域性變數也儲存在靜態資料區。在靜態資料區,記憶體中所有的位元組預設值都是0x00,某些時候這一特點可以減少程式設計師的工作量。比如初始化一個稀疏矩陣,我們可以一個一個地把所有元素都置0,然後把不是0的幾個元素賦值。如果定義成靜態的,就省去了一開始置0的操作。再比如要把一個字元陣列當字串來用,但又覺得每次在字元陣列末尾加’\0’太麻煩。如果把字串定義成靜態的,就省去了這個麻煩,因為那裡本來就是’\0’。不妨做個小實驗驗證一下。

#include <stdio.h>
int a;
int main(void)
{
    int i;
    static char str[10];
     printf("integer: %d;   string: (begin)%s(end)", a, str);
    return 0;
}

程式的執行結果如下

integer: 0; string: (begin)(end)

最後對static的三條作用做一句話總結。首先static的最主要功能是隱藏,其次因為static變數存放在靜態儲存區,所以它具備永續性和預設值0。

另外:

一、c程式儲存空間佈局

  C程式一直由下列部分組成:

  1)正文段——CPU執行的機器指令部分;一個程式只有一個副本;只讀,防止程式由於意外事故而修改自身指令;

  2)初始化資料段(資料段)——在程式中所有賦了初值的全域性變數,存放在這裡。

  3)非初始化資料段(bss段)——在程式中沒有初始化的全域性變數;核心將此段初始化為0。

4)棧——增長方向:自頂向下增長;自動變數以及每次函式呼叫時所需要儲存的資訊(返回地址;環境資訊)。

  5)堆——動態儲存分。

|-----------|
| |
|-----------|
| 棧 |
|-----------|
| | |
| |/ |
| |
| |
| /| |
| | |
|-----------|
| 堆 |
|-----------|
| 未初始化 |
|-----------|
| 初始化 |
|-----------|
| 正文段 |
|-----------|

  二、 面向過程程式設計中的static

  1. 全域性靜態變數

  在全域性變數之前加上關鍵字static,全域性變數就被定義成為一個全域性靜態變數。

  1)記憶體中的位置:靜態儲存區(靜態儲存區在整個程式執行期間都存在)

  2)初始化:未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)

  3)作用域:全域性靜態變數在宣告他的檔案之外是不可見的。準確地講從定義之處開始到檔案結尾。

  定義全域性靜態變數的好處:

  <1>不會被其他檔案所訪問,修改

  <2>其他檔案中可以使用相同名字的變數,不會發生**。

  2. 區域性靜態變數

  在區域性變數之前加上關鍵字static,區域性變數就被定義成為一個區域性靜態變數。

  1)記憶體中的位置:靜態儲存區

  2)初始化:未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)

  3)作用域:作用域仍為區域性作用域,當定義它的函式或者語句塊結束的時候,作用域隨之結束。

  注:當static用來修飾區域性變數的時候,它就改變了區域性變數的儲存位置,從原來的棧中存放改為靜態儲存區。但是區域性靜態變數在離開作用域之後,並沒有被銷燬,而是仍然駐留在記憶體當中,直到程式結束,只不過我們不能再對他進行訪問。

  當static用來修飾全域性變數的時候,它就改變了全域性變數的作用域(在宣告他的檔案之外是不可見的),但是沒有改變它的存放位置,還是在靜態儲存區中。

  3. 靜態函式

  在函式的返回型別前加上關鍵字static,函式就被定義成為靜態函式。

  函式的定義和宣告預設情況下是extern的,但靜態函式只是在宣告他的檔案當中可見,不能被其他檔案所用。

  定義靜態函式的好處:

  <1> 其他檔案中可以定義相同名字的函式,不會發生**

  <2> 靜態函式不能被其他檔案所用。

儲存說明符auto,register,extern,static,對應兩種儲存期:自動儲存期和靜態儲存期。

  auto和register對應自動儲存期。具有自動儲存期的變數在進入宣告該變數的程式塊時被建立,它在該程式塊活動時存在,退出該程式塊時撤銷。

  關鍵字extern和static用來說明具有靜態儲存期的變數和函式。用static宣告的區域性變數具有靜態儲存持續期(static storage duration),或靜態範圍(static extent)。雖然他的值在函式呼叫之間保持有效,但是其名字的可視性仍限制在其區域性域內。靜態區域性物件在程式執行到該物件的宣告處時被首次初始化。

  擴充套件分析:

  術語static有著不尋常的歷史.起初,在C中引入關鍵字static是為了表示退出一個塊後仍然存在的區域性變數。隨後,static C中有了第二種含義:用來表示不能被其它檔案訪問的全域性變數和函式。為了避免引入新的關鍵字,所以仍使用static關鍵字來表示這第二種含義。最後,

  C++重用了這個關鍵字,並賦予它與前面不同的第三種含義:表示屬於一個類而不是屬於此類的任何特定物件的變數和函式(與Java中此關鍵字的含義相同)。

C語言程式可以看成由一系列外部物件構成,這些外部物件可能是變數或函式。而內部變數是指定義在函式內部的函式引數及變數。外部變數定義在函式之外,因此可以在許多函式中使用。由於C語言不允許在一個函式中定義其它函式,因此函式本身只能是“外部的”。
       由於C語言程式碼是以檔案為單位來組織的,在一個源程式所有原始檔中,一個外部變數或函式只能在某個檔案中定義一次,而其它檔案可以通過extern宣告來訪問它(定義外部變數或函式的原始檔中也可以包含對該外部變數的extern宣告)。
       而static則可以限定變數或函式為靜態儲存。如果用static限定外部變數與函式,則可以將該物件的作用域限定為被編譯原始檔的剩餘部分。通過 static限定外部物件,可以達到隱藏外部物件的目的。因而,static限定的變數或函式不會和同一程式中其它檔案中同名的相沖突。如果用 static限定內部變數,則該變數從程式一開始就擁有記憶體,不會隨其所在函式的呼叫和退出而分配和消失。
   C語言中使用靜態函式的好處:

   1.        靜態函式會被自動分配在一個一直使用的儲存區,直到退出應用程式例項,避免了呼叫函式時壓棧出棧,速度快很多。
   2.        關鍵字“static”,譯成中文就是“靜態的”,所以內部函式又稱靜態函式。但此處“static”的含義不是指儲存方式,而是指對函式的作用域僅侷限於本檔案。 使用內部函式的好處是:不同的人編寫不同的函式時,不用擔心自己定義的函式,是否會與其它檔案中的函式同名,因為同名也沒有關係。

    c語言中static的語義
    1.static變數:
    1).區域性
    a.靜態區域性變數在函式內定義,生存期為整個源程式,但作用域與自動變數相同,只能在定義該變數的函式內使用。退出該函式後, 儘管該變數還繼續存在,但不能使用它。
    b.對基本型別的靜態區域性變數若在說明時未賦以初值,則系統自動賦予0值。而對自動變數不賦初值,則其值是不定的。
    2).全域性
    全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。但是他們的作用域,非靜態全域性 變數的作用域是整個源程式(多個原始檔可以共同使用); 而靜態全域性變數則限制了其作用域, 即只在定義該變數的原始檔內有效, 在同一源程式的其它原始檔中不能使用它。
    2.static函式(也叫內部函式)
    只能被本檔案中的函式呼叫,而不能被同一程式其它檔案中的函式呼叫。區別於一般的非靜態函式(外部函式)
         static在c裡面可以用來修飾變數,也可以用來修飾函式。
             先看用來修飾變數的時候。變數在c裡面可分為存在全域性資料區、棧和堆裡。其實我們平時所說的堆疊是棧而不包含對,不要弄混。
            int a ;
            main()
            {
                 int b ;
                 int c* = (int *)malloc(sizeof(int));
            }
            a是全域性變數,b是棧變數,c是堆變數。
            static對全域性變數的修飾,可以認為是限制了只能是本檔案引用此變數。有的程式是由好多.c檔案構成。彼此可以互相引用變數,但加入static修飾之後,只能被本檔案中函式引用此變數。
            static對棧變數的修飾,可以認為棧變數的生命週期延長到程式執行結束時。一般來說,棧變數的生命週期由OS管理,在退棧的過程中,棧變數的生命也就結束了。但加入static修飾之後,變數已經不在儲存在棧中,而是和全域性變數一起儲存。同時,離開定義它的函式後不能使用,但如再次呼叫定義它的函式時,它又可繼續使用, 而且儲存了前次被呼叫後留下的值。
           static對函式的修飾與對全域性變數的修飾相似,只能被本檔案中的函式呼叫,而不能被同一程式其它檔案中的函式呼叫。
          static 宣告的變數在C語言中有兩方面的特徵:
      1)、變數會被放在程式的全域性儲存區中,這樣可以在下一次呼叫的時候還可以保持原來的賦值。這一點是它與堆疊變數和堆變數的區別。
      2)、變數用static告知編譯器,自己僅僅在變數的作用範圍內可見。這一點是它與全域性變數的區別。