1. 程式人生 > >關於const,static,extern,volatile的用法

關於const,static,extern,volatile的用法

一.const的用法:
為什麼使用const?
採用符號常量寫出的程式碼更容易維護;指標常常是邊讀邊移動,而不是邊寫邊移動;許多函式引數是隻讀不寫的。const最常見用途是作為陣列的界和switch分情況標號(也可以用列舉符代替)

用法1:常量
   取代了C中的巨集定義,宣告時必須進行初始化。const限制了常量的使用方式,並沒有描述常量應該如何分配。如果編譯器知道了某const的所有使用,它甚至可以不為該const分配空間。最簡單的常見情況就是常量的值在編譯時已知,而且不需要分配儲存。―《C++ Program Language》
   用const宣告的變數雖然增加了分配空間,但是可以保證型別安全。C標準中,const定義的常量是全域性的,C++中視宣告位置而定。

用法2:指標和常量

   使用指標時涉及到兩個物件:該指標本身和被它所指的物件。將一個指標的宣告用const“預先固定”將使那個物件而不是使這個指標成為常量。要將指標本身而不是被指物件宣告為常量,必須使用宣告運算子*const所以出現在 * 之前的const是作為基礎型別的一部分:
char *const cp; //到char的const指標

(後兩個宣告是等同的)

char const *pc1; //到const char的指標
const char *pc2; //到const char的指標
從右向左讀的記憶方式:
cp is a const pointer to char.
pc2 is a pointer to const char.

用法3:const修飾函式傳入引數


    將函式傳入引數宣告為const,以指明使用這種引數僅僅是為了效率的原因,而不是想讓呼叫函式能夠修改物件的值。同理,將指標引數宣告為const,函式將不修改由這個引數所指的物件。
    通常修飾指標引數和引用引數
void Fun(const A *in); //修飾指標型傳入引數
void Fun(const A &in); //修飾引用型傳入引數

//注意引用的概念,我得C學得不夠好!icon


用法4:修飾函式返回值

   可以阻止使用者修改返回值。返回值也要相應的付給一個常量或常指標。



用法5:const修飾成員函式

//這個地方也理解不好啊!icon

  const物件只能訪問const成員函式,而非const物件可以訪問任意的成員函式,包括const成員函式;


  const物件的成員是不能修改的,而通過指標維護的物件確實可以修改的;
  const成員函式不可以修改物件的資料,不管物件是否具有const性質。編譯時以是否修改成員資料為依據進行檢查。


      2.static的用法:

全域性靜態變數怎麼用??

所謂的全域性靜態變數只在當前程式中有效。比如在a.c中定義了
static int i;
則只有a.c中的函式可以訪問i,其它程式模組的無法直接訪問這個變數,但可以通過a.c中所定義的函式間接訪問。比如在a.c中定義兩個函式:
int get_i()
{
return i;
};

int set_i( int n )
{
i=n;
}

以此獲得及重新設定i的值。這在c環境下是一種較好的模擬C++風格的實現方法。因為你可以將a.c看作一個類,i看作a的成員,而get_i()和set_i()則看作這個類的成員函式。

靜態變數作用範圍在一個檔案內,程式開始時分配空間,結束時釋放空間,預設初始化為0,使用時可以改變其值。
靜態變數或靜態函式只有本檔案內的程式碼才能訪問它,它的名字在其它檔案中不可見。
用法1:函式內部宣告的static變數,可作為物件間的一種通訊機制
如果一區域性變數被宣告為static,那麼將只有唯一的一個靜態分配的物件,它被用於在該函式的所有呼叫中表示這個變數。這個物件將只在執行執行緒第一次到達它的定義使初始化。
用法2:區域性靜態物件
對於區域性靜態物件,建構函式是在控制執行緒第一次通過該物件的定義時呼叫。在程式結束時,區域性靜態物件的解構函式將按照他們被構造的相反順序逐一呼叫,沒有規定確切時間。
用法3:靜態成員和靜態成員函式
如果一個變數是類的一部分,但卻不是該類的各個物件的一部分,它就被成為是一個static靜態成員。一個static成員只有唯一的一份副本,而不像常規的非static成員那樣在每個物件裡各有一份副本。同理,一個需要訪問類成員,而不需要針對特定物件去呼叫的函式,也被稱為一個static成員函式。
類的靜態成員函式只能訪問類的靜態成員(變數或函式)。

        3.extern的用法:
extern可以宣告其他檔案內定義的變數。在一個程式裡,一個物件只能定義一次,它可以有多個宣告,但型別必須完全一樣。如果定義在全域性作用域或者名字空間作用域裡某一個變數沒有初始化,它會被按照預設方式初始化。
將變數或函式宣告成外部連結,即該變數或函式名在其它函式中可見。被其修飾的變數(外部變數)是靜態分配空間的,即程式開始時分配,結束時釋放。
在C++中,還可以指定使用另一語言連結,需要與特定的轉換符一起使用。
extern “C” 宣告語句
extern “C” { 宣告語句塊 }

        4.volatile的用法:
     型別修正符(type-modifier),限定一個物件可被外部程序(作業系統、硬體或併發程序等)改變。volatile與變數連用,可以讓變數被不同的執行緒訪問和修改。宣告時語法:int volatile vInt;

    除了基本型別外,對使用者定義型別也可以用volatile型別進行修飾。
注意:可以把一個非volatile int賦給volatile int,但是不能把非volatile物件賦給一個volatile物件。
一個有volatile識別符號的類只能訪問它介面的子集,一個由類的實現者控制的子集。使用者只能用const_cast來獲得對型別介面的完全訪問。此外,volatile向const一樣會從類傳遞到它的成員。

        volatile的本意是“易變的”

       由於訪問暫存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。比如:

bit bFlag="0";

int main(void)
{
...
while (1)
{
if (bFlag) dosomething();
}
}

/* 中斷程式*/
void ISR(void)
{
bFlag=1;
}

程式的本意是希望ISR中斷產生時,在main當中呼叫dosomething函式,但是,由於編譯器判
斷在main函式裡面沒有修改過bFlag,因此
可能只執行一次對從bFlag到某暫存器的讀操作,然後每次if判斷都只使用這個暫存器裡面
的“bFlag副本”,導致dosomething永遠也不會被呼叫。如果將將變數加上volatile修飾,
則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。此例中bFlag也應該如此說
明。

        volatile是一個限定符,也稱為keyword或描述符,"volatile 關鍵字指示欄位可由作業系統、硬體或併發執行的執行緒在程式中進行修改。"

當要求使用volatile 宣告的變數的值的時候,系統總是重新從它所在的記憶體讀取資料,即使它前面的指令剛剛從該處讀取過資料。而且讀取的資料立刻被儲存。


一般說來,volatile用在如下的幾個地方:

1、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;

2、多工環境下各任務間共享的標誌應該加volatile;

3、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義; 


宣告方式為  volatile declaration


備註
系統總是在 volatile 物件被請求的那一刻讀取其當前值,即使上一條指令從同一物件請求值。而且,該物件的值在賦值時立即寫入。

volatile 修飾符通常用於由多個執行緒訪問而不使用 lock 語句來序列化訪問的欄位。使用 volatile 修飾符能夠確保一個執行緒檢索由另一執行緒寫入的最新值。

      另外,以上這幾種情況經常還要同時考慮資料的完整性(相互關聯的幾個標誌讀了一半被打斷了重寫),在1中可以通過關中斷來實現,2中可以禁止任務排程,3中則只能依靠硬體的良好設計了。

     volatile 的含義
     volatile總是與優化有關,編譯器有一種技術叫做資料流分析,分析程式中的變數在哪裡賦值、在哪裡使用、在哪裡失效,分析結果可以用於常量合併,常量傳播等優化,進一步可以死程式碼消除。但有時這些優化不是程式所需要的,這時可以用volatile關鍵字禁止做這些優化,volatile的字面含義是易變的,它有下面的作用:

     1 不會在兩個***作之間把volatile變數快取在暫存器中。在多工、中斷、甚至setjmp環境下,變數可能被其他的程式改變,編譯器 自己無法知道,volatile就是告訴編譯器這種情況。

     2 不做常量合併、常量傳播等優化,所以像下面的程式碼:
        volatile int i = 1;
        if (i > 0) ...

        if的條件不會當作無條件真。

     3 對volatile變數的讀寫不會被優化掉。如果你對一個變數賦值但後面沒用到,編譯器常常可以省略那個賦值***作,然而對Memory Mapped IO的處理是不能這樣優化的。

     一個網友說:volatile的意思是什麼?
很多時候,全域性變數不一定是全域性的,在多執行緒環境下可能產生微妙的錯誤,很有可能編譯器為了優化,而把一個全域性變數放入暫存器裡。volatile修飾符就是明確告訴編譯器,你他媽不準把這個變數優化到暫存器上,只能放記憶體裡