Re: [問題] Volatile陷阱

作者: tinlans ( )   2015-08-15 05:44:48
※ 引述《yshihyu (yshihyu)》之銘言:
: http://adrianhuang.blogspot.tw/2011/08/cvolatile.html
: 程式碼我是看這網站
你應該去看看它下面的參考資料
【Reference】
[1] How to Use C's volatile Keyword
http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
至於 blogspot 那篇建議別看,因為那是作者讀完參考資料以後自己隨便舉的例。
: #include <stdio.h>
: int square(volatile int *var)
: {
: return *var **var;
: }
: int main(void)
: {
: int var = 5;
: printf("result: %d\n", square(&var));
: return 0;
: }
這個例子最失敗的地方就是放了 main function,
然後 main function 又不經大腦思索就隨便亂寫。
這代表了這個例子是個完整程式,square() 的唯一 caller 就是 main()。
因為 var 必然是 main() 的 stack frame 裡存放 local variable 的某個 slot,
而這個 slot 的 address 是由 compiler 去決定的,
code 裡也沒任何特殊的元素去指明 var 應該被怎樣特別對待,
所以這個 var 的值壓根就不會有任何 compiler 意料之外的變動。
既然他根本懶得設計 square() 的 caller,其實他只放這樣就能暗示無限多種可能:
int sqaure(volatile int *var)
{
return *var * *var;
}
: 其問題在於square函式的平方算式,*var**var,此指令代表到var位址讀取其內容。然而
: ,var位址可能儲存硬體暫存器,這些暫存器內容會隨時間而改變 (例如: 狀態暫存器),
: 有可能第一次讀取的時候為4, 下一次讀取為5, 導致計算出來的值不正確。
: 可是前面又提到 凡牽涉讀取該volatile變數的操作,保證會到該變數的實體位址讀取,而不會讀取CPU暫
: 存器的內容 (提升效能) , 這樣這程式碼變數加上 volatile 都會從記憶體取值,這樣不是沒問題?
: 怎麼感覺講法有衝突,還是我誤解他意思?
:
: 他說正確解法是在square函式宣告一local變數
: int square(volatile int *var)
: {
: int local_var = *var;
: return local_var * local_var;
: }
: 謝謝
所以大家常說要讀書就要讀外文書或中譯本。
因為外國的月亮雖然未必真的比較圓,但是事實上卻常常比本國的圓很多很多。
volatile int *var 代表什麼意思?
代表 var 是一個 pointer,指向一個 volatile int 的資料。
換句話說是被指之物 (位於記憶體上) 具備 volatile 的特性,
而不是 var (也就是 pointer 本身) 具備 volatile 的特性。
來讀一下該文作者寫了什麼東西:
「其問題在於square函式的平方算式,*var**var,此指令代表到var位址讀取其內容。
然而,var位址可能儲存硬體暫存器,這些暫存器內容會隨時間而改變
(例如: 狀態暫存器),有可能第一次讀取的時候為4, 下一次讀取為5,
導致計算出來的值不正確。 」
既然他本段中寫到「*var**var」,那麼這所指的 var 必為 sqaure() 內的 var。
什麼叫「var位址」?這是中文的模稜兩可之處,所以中文技術文章不應該用這種用語。
如果解讀成「var 的位址」,那麼顯然不通,因為這段程式沒有取 var 的位址。
但是如果讀者執意這麼認為,他就會把這個當成 main function 的 var,產生混亂。
這個「var位址」真要寫的話,應該寫成「位址var」比較正確。
而作者之所以這樣寫,只是想表達 var 是個 pointer 罷了,但根本沒必要。
「var位址可能儲存硬體暫存器,這些暫存器內容會隨時間而改變」這也是很糟的句子。
我不管怎麼讀,都很像是程式把其它國語言硬翻成中文一樣,完全不通順。
「var位址」是主詞,「儲存」是動詞,「硬體暫存器」是受詞。
世界上有什麼 pointer 或 address 本身可以拿來「儲存」硬體暫存器?沒有。
根據他的參考資料,可以發現這段想說的叫做 memory-mapped registers。
因為各位很多是大學直接讀外文書,可能很少人知道 memory-mapped 的正式譯法。
這個形容詞的翻譯方式叫「記憶體映射」,所以上文的「儲存」應當寫成「映射」。
也就是說,前文的「硬體暫存器」所指的暫存器,並不是很多讀者誤會的那種暫存器。
每次讀取 *var 的動作,可能是從某個週邊裝置循序讀取一筆資料;
每次寫入 *var 的動作,可能是對某個週邊裝置循序寫入一筆資料。
意思是 *var 這塊空間可能是被投射到該週邊裝置上的某個暫存器上,
該週邊裝置可能定時檢查這個暫存器的值有無變化,有變化就會進行某些操作;
讀取該暫存器的動作,可能觸發該週邊裝置去執行某項操作,然後把一個值更新進去;
該週邊裝置可能不斷在做某件事,不斷更新該暫存器的內容,所以值常常讀出來不一樣。
這文章的作者第一段把兩種暫存器混在一起,又沒有特別提及 memory-mapped 這關鍵字,
坦白說我有點懷疑他自己其實不懂,也沒讀通他參考原文的意思。
不過姑且可以理解成他只讀外文書,所以中文文章不知道該怎麼寫了吧。
據說這是某公司的面試考題,如果這是真的...
我只能說直接把網路上的東西複製下來當考題,這出題者也實在太沒誠意了。
作者: firose (guest也是也是也是也是也)   2015-08-15 06:57:00
推~ 之前看到 main 就一直覺得這題很怪 XD
作者: kwpn (ITSST)   2015-08-15 17:11:00
真的是國外的比中文的圓超多,工作數月後就不再看中文書籍了
作者: CLANNAD (-クラナド-)   2015-08-15 20:43:00
大神好久沒發文了
作者: askacis (ASKA)   2015-08-16 08:06:00
沒錯,這題真的爛到不行
作者: name2name2 (yang~hi)   2015-08-16 09:51:00
作者: anyoiuo   2015-08-17 14:51:00
透過WriteProcessMemory API,由不同process去修改var變數var就會有意料之外的變動
作者: cobrasgo (人魚線變成鮪魚線,超帥)   2015-08-17 22:49:00
原本有main我實在是看不出有什麼問題…不加main就有很多case可以討論了

Links booklink

Contact Us: admin [ a t ] ucptt.com