2015-07-20 74 views
3

易失性寫入易失性常量是否引入未定義的行爲?如果我在寫作時跌跌撞撞地怎麼辦?volatile變量的間接變化可以視爲未定義的行爲?

volatile const int x = 42; 
const volatile int *p = &x; 
*(volatile int *)p = 8; // Does this line introduce undefined behavior? 
*(int *)p = 16; // And what about this one? 

Compilable code

+0

相關到[如何在同一地址產生2個不同的值的變量?](http://stackoverflow.com/q/22656734/1708801) –

回答

8

這是不確定的行爲(兩個語句),你試圖修改 「初始」 const對象。從C11(N1570)6.7.3/P6 類型限定符(重點煤礦):

如果試圖修改通過使用左值的與 常量限定類型定義的對象與非-const-qualified 類型,行爲是未定義的。

爲了完整性它可能是值得增加,即標準也說:

如果試圖指通過使用左值的與具有 volatile限定類型定義的對象 非易失性限定類型,行爲未定義。

因此後者語句,即:

*(int *)p = 16; 

是未定義第二短語,以及(這是一個 「雙UB」)。

我相信C++的規則是一樣的,但是並不擁有C++ 14的副本來確認。

+0

像我的回答說,修改一個'const'值是無效的 - 但是,它刪除'volatile'無效 - 它只會讓編譯器潛在地緩存全局值。 –

+0

@MatsPetersson:希望我的回答現在更清楚。是的,你是對的,你可以放棄'volatile'限定詞,這不是UB。 –

+0

而常量被聲明爲volatile的事實不影響這種說法嗎? – Qwertiy

5

寫入最初爲const的變量是未定義的行爲,因此您所有寫入*p的示例均未定義。

刪除volatile本身並不是未定義的。

但是,如果我們有像const volatile int *p = (const volatile int*)0x12340000; /* Address of hw register */這樣的東西,那麼刪除volatile可能會導致硬件更新寄存器值,但是您的程序不會檢測到它。 (例如,如果我們「忙於等待」while(*p & 0x01) ;,編譯器應該每次都重新加載p指向的值,但while((*(const int *)p) & 1) ;,編譯器完全可以自由讀取值1,並且如果有一點再次使用初始值循環0被設定)。

你當然可以有 extern volatie int x;然後用 const volatile int *p = &x;在一些代碼,並 x得到您的當前轉換單元之外的一些其他的代碼更新(例如另一個線程) - 在這種情況下刪除或者 constvolatile是有效,但如上所述,您可能會「錯過」更新的值,因爲除非您調用函數,否則編譯器不希望全局值在模塊外更新。 [編輯,不,拿走volatile通過鑄造該值也是被禁止的標準 - 但是將constvolatile添加到某個東西然後再刪除它,如果它原來的對象沒有它]。

需要Edti2:volatile來告訴編譯器「即使您認爲什麼都不應該改變它,值可能隨時改變」。發生這種情況,在一般情況下,在兩種情況:即在軟件的外面完全更新

  1. 硬件寄存器 - 例如用於串行端口,定時器計數器寄存器狀態寄存器,或中斷的中斷控制器的狀態,舉幾個例子 - 有成千上萬的其他變體,但它們都是一樣的想法:硬件改變了數值,與訪問這些寄存器的軟件沒有直接關係。
  2. 變量由進程內的另一個線程(或在共享內存的情況下,由另一個進程)更新 - 再次,編譯器將無法「看到」這樣的變化。通常情況下,可以通過調用wait-loops等內部的函數來編寫看起來能夠正常工作的代碼,編譯器會重新載入非本地變量的值,但一段時間後編譯器決定內聯該函數調用,並且然後意識到代碼沒有更新該值,因此不會重新加載值 - >當您檢查「更新」值並找到與編譯器之前已加載的舊值相同的值時。也

注意volatile不是任何種類的線程/進程的正確性的保障 - 它只是保證編譯器不會跳過讀取或寫入該變量。程序員仍然需要確保例如依賴於排序的多個值確實以正確的順序更新,並且在高速緩存在處理單元之間不一致的系統上,通過軟件使高速緩存一致[for例如,一個CPU和一個GPU可能不會使用連貫的內存更新,因此CPU寫入直到CPU上的緩存已被刷新纔會寫入GPU - 不會將代碼volatile應用於此代碼將修復此問題]

+0

你是什麼意思的線程?如果我將相同的代碼放入另一個線程並等待完成,代碼是否會正確?或者你的意思是這個過程? – Qwertiy

+1

你是什麼意思刪除。如果你的意思是指向那麼它也是未定義的。 *如果嘗試 是通過使用非易失性限定類型的左值 來引用通過易失性限定類型定義的對象,則行爲未定義。* – this

+0

@This:Ok,已更新。 –