C中的auto、static、register、extern、const和volitate

前進的菜鳥發表於2012-08-02
C語言中的每一個變數和函式有兩個屬性:資料型別和資料的儲存類別。資料型別(整形、字元型等),儲存類別是指資料在記憶體中儲存的方法,儲存方法有兩大類:靜態儲存類和動態儲存類。具體包括四種:自動的(auto),靜態的(static),暫存器的(register)和外部的(extern)。
       auto變數:函式中的區域性變數,如不專門宣告static,一般都是動態地分配儲存空間。自動變數:在呼叫該函式時系統會給他們分配儲存空間,一旦函式呼叫結束這些儲存空間就會自動釋放。關鍵字“auto”可以省略,不寫則隱含確定為“自動儲存類別”,屬於動態儲存方式。
       static宣告變數:用static宣告的靜態區域性變數,在函式呼叫結束後不消失,反而保留當前的資料,在下一次該函式呼叫時,該變數現有的值就是上一次函式呼叫結束時的值。
    一般用static宣告一個變數的作用有二:(1)對區域性變數用static宣告,則為該變數分配的空間在整個程式執行期間始終存在。(2)對全部變數用static宣告,則該變數的作用域只限於本檔案模組,即被宣告的檔案中。
eg:f(int a)
       {
           auto b=0;               //將b定義為auto型別。
           static c=3;              //將c定義為static型別。
           b=b+1,c=c+1;
           return(a+b+c);
        }
      main()
       {
        int a=2,i;
        for(i=0;i<3;i++)
         printf("%d",f(a));
       }
      在第一次呼叫f函式時b=0,c=3,第一次呼叫結束後b=1,c=4,a+b+c=7;執行完之後由於c是靜態區域性變數,在函式呼叫結束後,它並不釋放,所以保留c=4。而b還是0。所以程式輸出7,8,9。
   static還可以宣告函式,eg:static int fun(int a, int b)稱fun為內部函式,或者靜態函式。內部函式的使用只限於所在檔案,而且不同檔案中的同名內部函式互不干擾。
      register變數:一般變數的值都是儲存在記憶體中,(當程式需要用到哪一個變數的值,由控制器發出指令將記憶體中該變數的值送到運算器,完了如果需要存數,再從運算器將資料送到記憶體中存放。)所以就引出一個問題,如果我們進行一段頻繁的運算,則儲存變數的值肯定要花費不少時間,所以C語言允許將區域性變數的值存放在暫存器中,這樣需要時就直接搬用,不必再進行過記憶體。提高運算速度。
      extern宣告外部變數:外部變數(即全域性變數)是在函式的外部定義的。作用域為從變數的定義處開始,到本程式檔案的結尾。可以在一個檔案內宣告外部變數,如:
     main()
     {
       extern A,B;
       printf("%d",max(A,B));
     } int A=13,B=-8;
     也可以在多檔案的程式中宣告外部變數。

    extern還可宣告函式,eg:extern int fun(int a, int b);宣告的外部函式可供其他檔案呼叫,在C中,定義函式時省略extern,則隱含為外部函式。

別人問起,不能簡單說const表示常數,這樣會讓別人覺得很外行。或許可以說是隻讀,其實也不完全正確。務必要弄清楚一下幾個定義的含義:
 
const int a;       //a是一個常整型數
int const a;      //a是一個整型常數
const int *a;    //a是一個指向常整型數的指標,從這裡可以看出整型數不可以修改,但指標可以。
int * const a;   //a是一個指向整型數的常指標,整型數可以修改,指標不能修改。
int const * a const;   //a是一個指向常整型數的常指標。


如果能正確回答這些問題,那麼他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字 const,也還是能很容易寫出功能正確的程式,那麼我為什麼還要如此看重關鍵


字const呢?我也如下的幾下理由:
1). 關鍵字const的作用是為給讀你程式碼的人傳達非常有用的資訊,實際上,宣告一個引數為常量是為了告訴了使用者這個引數的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會


很快學會感謝這點多餘的資訊。(當然,懂得用const的程式設計師很少會留下的垃圾讓別人來清理的。)
2). 通過給優化器一些附加的資訊,使用關鍵字const也許能產生更緊湊的程式碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的引數,防止其被無意的程式碼修改。簡而言之,這樣可以減少bug的出現。


關鍵字volatile有什麼含意 並給出三個不同的例子。


一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數


的值,而不是使用儲存在暫存器裡的備份。下面是volatile變數的幾個例子:
1). 並行裝置的硬體暫存器(如:狀態暫存器)
2). 一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)
3). 多執行緒應用中被幾個任務共享的變數
回答不出這個問題的人是不會被僱傭的。我認為這是區分C程式設計師和嵌入式系統程式設計師的最基本的問題。嵌入式系統程式設計師經常同硬體、中斷、RTOS等等打交道,所用這些都要求volatile變數


。不懂得volatile內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這傢伙是不是直正懂得volatile完全的重要性。
1). 一個引數既可以是const還可以是volatile嗎?解釋為什麼。
2). 一個指標可以是volatile 嗎?解釋為什麼。
3). 下面的函式有什麼錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是隻讀的狀態暫存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。
2). 是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指標時。
3). 這段程式碼的有個惡作劇。這段程式碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型引數,編譯器將產生類似下面的程式碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段程式碼可能返不是你所期望的平方值!正確的程式碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

相關文章