register
使用修飾符register聲明的變數屬於寄存器儲存類型(一般的都預設是儲存空間儲存類型,volatile關鍵字也是針對這種類型的)。該類型與自動儲存類型相似,具有自動儲存時期、代碼塊範圍和內串連。聲明為register僅僅是一個請求,因此該變數仍然可能是普通的自動變數。無論哪種情況,用register修飾的變數都無法擷取地址。如果沒有被初始化,它的值是未定的。
volatile
volatile告訴編譯器該被變數除了可被程式修改外,還可能被其他代理、線程修改。因此,當使用volatile 聲明的變數的值的時候,系統總是重新從它所在的記憶體讀取資料,而不使用寄存器中的緩衝的值。比如,
val1=x;
val2=x;
如果沒有聲明volatile,系統在給val2賦值的時候可能直接從寄存器讀取x,而不是從記憶體的初始位置讀取。那麼在兩次賦值之間,x完全有可能被被某些編譯器未知的因素更改(比如:作業系統、硬體或者其它線程等)。如果聲明為volatile,編譯器將不使用資料寄存器的緩衝,而是每次都從記憶體重新讀取x。
restrict
restrict是c99引入的,它只可以用於限定指標,並表明指標是訪問一個資料對象的唯一且初始的方式,考慮下面的例子:
123 |
int ar[10]; int * restrict restar=( int *) malloc (10* sizeof ( int )); int *par=ar; |
這裡說明restar是訪問由malloc()分配的記憶體的唯一且初始的方式。par就不是了。那麼:
12345678 |
for (n=0;n<10;n++) { par[n]+=5; restar[n]+=5; ar[n]*=2; par[n]+=3; restar[n]+=3; } |
因為restar是訪問分配的記憶體的唯一且初始的方式,那麼編譯器可以將上述對restar的操作進行最佳化:restar[n]+=8;。而par並不是訪問數組ar的唯一方式,因此並不能進行下面的最佳化:par[n]+=8;。因為在par[n]+=3前,ar[n]*=2進行了改變。使用了關鍵字restric,編譯器就可以放心地進行最佳化了。這個關鍵字據說來源於古老的FORTRAN。
總結
兩個關鍵字:volatile和restrict,兩者都是為了方便編譯器的最佳化。