compiler 的最佳化編譯,為了增快程式速度,
有時候會很聰明的將程式碼編譯成和原 code 不同,但意思相同的 object code
如下:
for (int i = 0; i < 10000; i++) {
a = 5;
a = a * i;
}
compiler 可以簡化成下列一條敘述:
a = 5 * 9999;
編譯器最佳化,就讓程式變得更有效率。
但是當我們某些程式片段不希望做最佳化的時候呢? 就要使用 volatile 關鍵字
volatile 是 C 的關鍵字,用來修飾資料型態,是告訴 compiler 不要對這一個變數的程式碼區段做最佳化。
volatile 表明某個變量的值可能在外部被改變,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器裡的備份。
它可以適用於基礎類 型如:int,char,long......也適用於C的結構和C++的類。當對結構或者類對象使用volatile修飾的時候,結構或者類的所有成員 都會被視為volatile.
volatile 使用時機
該關鍵字在多線程環境下經常使用,因為在編寫多線程的程序時,同一個變量可能被多個線程修改,而程序通過該變量同步各個線程。
簡單示例:
復制代碼代碼如下:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterdivt_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
該線程啟動時將intSignal 置為2,然後循環等待直到intSignal 為1 時退出。顯然intSignal的值必須在外部被改變,否則該線程不會退出。但是實際運行的時候該線程卻不會退出,即使在外部將它的值改為1,看一下對應的偽匯編代碼就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
對於C編譯器來說,它並不知道這個值會被其他線程修改。自然就把它cache在寄存器裡面。C 編譯器是沒有線程概念的,這時候就需要用到volatile。volatile 的本意是指:這個值可能會在當前線程外部被改變。也就是說,我們要在threadFunc中的intSignal前面加上volatile關鍵字,這時 候,編譯器知道該變量的值會在外部改變,因此每次訪問該變量時會重新讀取,所作的循環變為如下面偽碼所示:
label:
mov ax,signal
if(ax!=1)
goto label
注意:一個參數既可以是const同時是volatile,是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
restrict
restrict 是 C 的關鍵字,只能用來修飾 pointer,目的是協助 compiler 做最佳化
是告知 compiler ,這一個 restrict pointer 是唯一指向 data 的 pointer,
意即程式當中不會透過其他變數 ( pointer,或者直接存取 ) 來 access 此 data,
例子如下:
int * restrict rptr = (int*) malloc(10*sizeof(int));
int a[10];
int * ptr;
ptr = a;
for(int i =0; i<10;i++) {
ptr += 5;
rptr +=5;
a *= 5;
ptr += 3;
rptr += 3;
}
當 compiler 看到 rptr 為 restric,就會用 rptr += 8 取代 rptr += 3 和 rptr += 5
而 ptr 就不應該被直接替換,因為它不是唯一會 access 該 data 的變數
C99 用到restrict的例子:
void* memcpy (void* restrict destination, const void* restrict source, size_t n);
代表destination和source區段是不可重疊的
FILE *fopen(const char *restrict pathname, const char *restrict type);
https://www.itread01.com/content/1550372781.html
https://liquid0118.pixnet.net/blog/post/48494846