C C volatile型別限定符

2021-10-04 07:42:16 字數 2758 閱讀 3374

volatile 限定符告知計算機,**(而不是變數所在的程式)可以改變該變數的值。

通常,它被用於硬體位址以及在其他程式或同時執行的執行緒中共享資料。

例如,乙個位址上可能儲存著當前的時鐘時間,無論程式做什 麼,位址上的值都隨時間的變化而改變。或者乙個位址用於接受另一台計算 機傳入的資訊。

volatile的語法和const一樣:

volatile

int loc1;

/* loc1 是乙個易變的位置 */

volatile

int* ploc;

/* ploc 是乙個指向易變的位置的指標 */

以上**把loc1宣告為volatile變數,把ploc宣告為指向volatile變數的指標。

讀者可能認為volatile是個可有可無的概念,為何ansi委員把volatile關 鍵字放入標準?原因是它涉及編譯器的優化。例如,假設有下面的**:

vall =x;

/* 一些不使用 x 的***/

val2 = x

智慧型的(進行優化的)編譯器會注意到以上**使用了兩次 x,但並未改變它的值。於是編譯器把 x的值臨時儲存在暫存器中,然後在val2需要使 用x時,才從暫存器中(而不是從原始記憶體位置上)讀取x的值,以節約時間。

這個過程被稱為快取記憶體(caching)。

通常,快取記憶體是個不錯的優化方案,但是如果一些其他**在以上兩條語句之間改變了x的值,就不能這 樣優化了。

如果沒有volatile關鍵字,編譯器就不知道這種事情是否會發 生。因此,為安全起見,編譯器不會進行快取記憶體。這是在 ansi 之前的情 況。現在,如果宣告中沒有volatile關鍵字,編譯器會假定變數的值在使用 過程中不變,然後再嘗試優化**。

可以同時用const和volatile限定乙個值。

例如,通常用const把硬體時鐘 設定為程式不能更改的變數,但是可以通過**改變,這時用 volatile。只能在宣告中同時使用這兩個限定符,它們的順序不重要,如下所示:

volatile

const

int loc;

const

volatile

int* ploc;

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

下面通過插入彙編**,測試有無 volatile 關鍵字,對程式最終**的影響,輸入下面的**:

#include

void

main()

int b = i;

printf

("i = %d"

, b)

;}

然後,在 debug 版本模式執行程式,輸出結果如下:

i =

10i =

32

然後,在 release 版本模式執行程式,輸出結果如下:

i =

10i =

10

以下是引用的多執行緒程式設計:

volatile,臨界區和競爭條件

多執行緒程式中最簡單也是最常用的同步機制要算是mutex(互斥物件)了。乙個mutex只提供兩個基本操作:acquire和release。一旦某個執行緒呼叫了acquire,其他執行緒再呼叫acquire時就會被阻塞。當這個執行緒呼叫release後,剛才阻塞在acquire裡的執行緒中,會有乙個且僅有乙個被喚醒。換句話說,對於乙個給定的mutex,只有乙個執行緒可以在acquire和release呼叫之間獲取處理器時間。在acquire和release呼叫之間執行的**叫做臨界區(critical section)。(windows的用語可能會引起一點混亂,因為windows把mutex本身叫做臨界區,而windows的mutex實際上指程序間的mutex。如果把它們分別叫作執行緒mutex和程序mutex可能會好些。)

mutex是用來避免資料出現競爭條件。根據定義,所謂競爭條件就是這樣一種情況:多個執行緒對資料產生的作用要依賴於執行緒的排程順序的。當兩個執行緒競相訪問同一資料時,就會發生競爭條件。因為乙個執行緒可以在任意乙個時刻打斷其他執行緒,資料可能會被破壞或者被錯誤地解釋。因此,對資料的修改操作,以及有些情況下的訪問操作,必須用臨界區保護起來。在物件導向的程式設計中,這通常意味著你在乙個類的成員變數中儲存乙個mutex,然後在你訪問這個類的狀態時使用這個mutex。

多執行緒程式設計高手看了上面兩個段落,可能已經在打哈欠了,但是它們的目的只是提供乙個準備練習,我們現在要和volatile聯絡起來了。我們將把c++的型別和執行緒的語義作乙個對比。

在乙個臨界區以外,任意執行緒會在任何時間打斷別的執行緒;這是不受控制的,所以被多個執行緒訪問的變數容易被改得面目全非。這和volatile的原意[1]是一致的——所以需要用volatile來防止編譯器無意地快取這樣的變數。

在由乙個mutex限定的臨界區里,只有乙個執行緒可以進入。因此,在臨界區中執行的**有和單執行緒程式有相同的語義。被控制的變數不會再被意外改變——你可以去掉volatile修飾。

簡而言之,執行緒間共享的資料在臨界區之外是volatile的,而在臨界區之內則不是。

你通過對乙個mutex加鎖來進入乙個臨界區,然後你用const_cast去掉某個型別的volatile修飾,如果我們能成功地把這兩個操作放到一起,那麼我們就在c++型別系統和應用程式的執行緒語義建立起聯絡。這樣我們可以讓編譯器來幫我們檢測競爭條件。

linux volatile型別限定符

背景 一直會有意無意的看到有這個volatile,但是做為乙個在linux應用層寫了幾年 的程式設計師,還真的沒有搞懂什麼時候需要這樣用?查了一圈,網上都是各種告訴編譯器不要把該變數放到暫存器,放到暫存器,如果其它地方改了拿不到最新的值,不要優化等等 首先說下,組合語言真的屬於基本不會那種。多執行緒...

ANSIC型別限定符

以const型別關鍵字宣告的物件,其值不能通過賦值來遞增,遞減來修改。以下 const int a 12 限定a的值不能修改 a 12 不可以編譯器會報錯,但是可以初始化變數,因此下面的 沒問題 const int a 12 沒問題該宣告讓a成為唯讀變數。初始化後,就不能改變它的值。可以用const...

const 型別限定符

1.以const關鍵字宣告的物件,其值不能通過賦值或遞增 遞減來修改。例 const int a 限定a的值不能被修改 a 12 不允許 2.在指標中使用const const float pf pf指向乙個float型別的const,建立了pf指向的值不能更改 float const pt pt是...