1. 程式人生 > >C++| |volatile關鍵字

C++| |volatile關鍵字

volatile關鍵字

  • volatile的含義是“易變的”,含義是說這變數有可能會被意想不到的改變,這樣的話,編譯器就不會去假設這個變數的值,也就是不會為了優化到暫存器中讀取這個變數的值,會每次都到記憶體中讀取

  • 是一個型別修飾符。

  • 作用:

    • 作為指令的關鍵字,確保本條指令不會因為編譯器的優化,直接到記憶體中讀取變數的值

作用:

  • 簡單來說就是:防止編譯器對於程式碼進行優化

  • 比如:

    • variable = 1;
      variable = 2;
      variable = 3;
      variable = 4;
      ​
      //對於外部硬體來說,這四條語句分別表示不同的操作,會產生四種不同的動作,但是編譯器會對上述四條語句進行優化,認為只有variable = 4(即忽略前三條語句,只產生一條機器程式碼)。如果鍵入volatile的話,編譯器會逐一的進行編譯併產生相應的程式碼。

例子

  • 編譯器每次用到這個變數的時候必須小心的從重新讀取這個變數的值,而不是使用儲存在暫存器中的備份。

  • 以下是幾個例子:

    • 並行裝置的硬體暫存器

    • 一箇中斷服務子程式中會訪問到非自動變數

    • 多執行緒應用中被幾個任務共享的變數

  • 【面試題】:

    1. 一個引數既可以是const也可以是volatile嗎?解釋一下為什麼。

    • 可以。對於一個變數,它是volatile因為他可能被意想不到的改變,它是const因為程式不應該試圖去修改它

    1. 一個指標可以是volatile嗎?解釋一下為什麼?

    • 可以。因為指標也是變數與引數一樣

    1. 下面的函式可以實現計算某個整數的平方嗎?如果不能,試回答存在什麼問題?

    int square(volatile int* ptr)
    {
        return ((*ptr) * (*ptr));
    }
    • 這段程式碼是用來返回*ptr所指向的值的平方。但是,由於 *ptr指向的是一個volatile型的引數,編譯器將會產生類似下面的程式碼。

    int square(volatile int* ptr)
    {
        int a, b;
        a = *ptr;
        b = *ptr;
        return a * b;
    }
    • 由於ptr的值可能在兩次取值語句之間發生改變,因此a和b可能是不同的。

    • 正確的程式碼如下:

    long square(volatile int* ptr)
    {
        int a;
        a = *ptr;
        return a * a;
    }

    關鍵在於編譯器的優化

    • 在本次執行緒內,當讀取一個變數的時候,為提高存取速度,編譯器優化是有時會先把變數讀取到一個暫存器中去,以後再取變數值時,就直接從暫存器中取值。

    • 當變數在本執行緒裡改變時,會同時把變數的新值copy到暫存器中,以便保持一致

    • 當變數在因別的執行緒等而改變了值,該暫存器的值,不會相應改變,從而造成應用程式讀取的值和實際變數值不一致

    • 當該暫存器在因別的執行緒等改變了值,原變數的值不會改變,從而導致應用程式讀取的值和實際的變數值不一致

      • 舉個例子:

      • 發薪資是,會計每次都把員工叫來登記他們的銀行卡號:一次會計為了省事,沒有及時登記,用了以前登記的銀行卡號;剛好一個員工的銀行卡丟了,已掛失該銀行卡號,從而導致該員工領不到工資。

      • 員工----原始變數地址

      • 銀行卡號----原始變數在暫存器的備份

程式碼

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
​
volatile int flag = 0;
​
void handler(int signo)
{
  flag = 1;
  cout << "change flag to 1" <<  endl;
}
​
​
int main()
{
  //捕捉SIGINT訊號,然後執行自定義函式
  signal(SIGINT, handler);
  while (!flag)
  {
  }
​
  cout << "proc done..." << endl;
  return 0;
}