2017年3月14日 星期二

Volatile (int char ...) & const (int char ...)宣告變數

Volatile 

宣告變數 Volatile(揮發) 顧名思義揮發性的變數宣告。

在了解Volatile 之前必須先搞懂一件事,就是processes中變數的處理方式,簡單來說當program 中宣告,當宣告一個變數後此變數會暫存在CPU 的cache中,以方便快速的運算,直到processes結束。

Volatile 使用時機:
很多時候compiler 可以依據各種不同取向與需求而進行不同的最佳化,包括執行速度最快化、程式容量佔量最小化、針對某種硬體系統所能發揮運用的指令來最適化等。但是有時最佳畫會造成程式執行有誤,宣告volatile變數用來告訴編譯器(Compiler) 不要對該變數做任何最佳化操作,最常見的錯誤如以下兩種:

1, I/O

假設有一程式片斷如下

       U8   *pPort;
       U8   i, j, k;
     
       pPort = (U8 *)0x800000;
 
       i = *pPort;    
       j = *pPort;    
       k = *pPort;  

以上的i, j, k很有可能被compiler最佳化而導致產生
       i = j = k = *pPort;
的code, 也就是說只從pPort讀取一次, 而產生 i = j = k 的結果, 但是原本的程式的目的是要從同一個I/O port讀取3次的值給不同的變數, i, j, k的值很可能不同(例如從此    I/O port 讀取溫度), 因此i = j = k的結果不是我們所要的怎麼辦

用volatile, 將
       U8   *pPort;
改為
       volatile U8   *pPort;

告訴compiler, pPort變數具有揮發性的特性, 所以與它有關的程式碼請不要作最佳化動作. 因而
       i = *pPort;    
       j = *pPort;    
       k = *pPort;    
此三列程式所產生的code, 會真正地從pPort讀取三次, 從而產生正確的結果

2. Global variables in Multithread Program 

這是在撰寫multithread program時最容易被忽略的一部份
此原因所造成的bug通常相當難解決(因為不穩定)

假設有以下程式片斷, thread 1 & thread 2共用一個global var: gData
        thread 1:                                thread 2:                              
                                                                                       
            ...                                      ....                              
            int  gData;                              extern int gData;                  
                                                                                       
            while (1)                                int  i, j, k;                      
            {                                                                          
                ....                                 for (i = 0; i < 1000; i++)
                gData = rand();                      {                                  
                .....                                    /* A */
            }                                            j = gData;                    
                                                         ....                          
            ....                                     }                                

在thread 2的for loop中, 聰明的compiler看到gData的值, 每次都重新從memory load到register,
實在沒效率, 因此會產生如下的code(注意,tmp也可以更進一步的用register取代):
       tmp = gData;
       for (i = 0; i < 1000; i++          
       {                                  
           /* A */
           j = tmp;                    
           ....                          
       }                                  
也就是gData只讀取一次, 這下子問題來了, 說明如下:
thread 2在執行for loop到j = gData的前一列(A)的時候(假設此時gData=tmp=5),
被切換到thread 1執行在thread 1的while loop中透過gData = rand(), 對gData做了修改(假設改為1), 再切換回thread2執行
繼續執行 j = gData, 產生j = 5的結果
但是正確的結果應該是 j = 1
怎麼辦 => 也是用volatile

在thread 1中, 將
        int  gData;
改為
        volatile int  gData;
 
在thread 2中, 將
        extern int  gData;
改為
        extern volatile int  gData;

Const

用來定義常數 (constant) ,凡是以 const 宣告後的變數 (variable) 設定初值後,都不能重新指派新的值。一般來說大家會把他解讀為『常數』,但是這樣的解釋好像有點點不足,應該要解讀為 read-only 比較恰當。

const 與 #define 又有甚麼不同呢?

#define =>可建立巨集 Macro, 或宣告常用常數使用是一種pre-compile header
會在程式編譯成機械語言前先編譯好所以程式在執行時會較快 較省空間(但也有說法 現代的編譯器用define or const所編出來的code品質是相當的)

const =>宣告常數使用(且是一個可定址的常數資料)
常數欄位和區域常數不是變數,可能無法修改。

常數可以是數值、布林值、字串或 null 參考。 請勿建立用來表示想隨時變更之資訊的常數。

reference:
C/C++中的volatile使用時機

沒有留言:

張貼留言

linux - IPC (inter-processes communication) Shered mempry(共用記憶體)

IPC (inter-processes communication) 顧名思義,processes 之間溝通的管道&機制。 Shared memory   程式間可以共享memory Message Queue  程式間傳送資訊最簡單的方法 Semaphore...