Re: [問題] 關於Undefined Behavior的一些疑問

看板C_and_CPP作者 (小西風最乖了*^^*)時間14年前 (2011/07/06 04:45), 編輯推噓3(3017)
留言20則, 5人參與, 最新討論串1/1
好像是被我推坑的 xD ※ 引述《angleevil (human)》之銘言: : 1. : 最近在看http://blog.regehr.org/archives/213 : 裡面有討論一些Undefined Behavior,但是有些不是很懂, : 因此來詢問大家, : 為什麼a變數要檢查INT32_MIN呢? : b為-1會有什麼問題? : : int32_t safe_div_int32_t (int32_t a, int32_t b) : { : if ((b == 0) || ((a == INT32_MIN) && (b == -1))) : { : report_integer_math_error(); : return 0; : } : else : { : return a / b; : } : } : : → akasan:INT32_MIN/-1 = INT32_MAX'+1' 07/06 10:10 : → akasan:在一般二補數系統會發生 07/06 10:10 補充:C99 強制堆定 INTN_MIN 剛好 -2^(N-1) 然後 INTN_MAX 剛好 2^(N-1)-1. 即使在某些少見的實作中 int 不用二補數,但 int32_t 一定 是佔了剛好 32 位元,用二補數表示的有號整數。標準的意思是說沒支援 不提供沒關係,想支援就按照規矩來。這就是作者有信心這份程式碼絕對 安全的原因。(在詭異的實作上頂多編譯失敗,執行期間是有保證的。) : 2. : 還有所謂的side effect是指a=b++ + ++b的問題嘛? : 因為我好像一值以來,把它和bound問題混在一起想, : 作者用asm當例子,我就更難理解了! 不完全是。副作用(side effect)指的是除了計算出結果以外,其他 「看得到」的效果。例如我今天請你計算 1 + 1, 結果你先跑去引爆核彈 再回頭告訴我答案是 2,然後我也看到地球毀了,那引爆核彈就是副作用。 這有兩個重點,一個是「計算以外」,一個是「看得到」(observable)。 C99 和 C++03 定義了好幾樣東西都是副作用(C99 5.1.2.3 C++03 1.9), 其中一樣講白話就是修改變數中的內容。 blog 文章第三篇我個人感覺重點有兩個。第一個是,通常如果你這樣寫: | 敘述1; | 敘述2; 由於分號的關係,標準規定所有敘述1的副作用(例如引爆核彈)一定 要在分號前完成(要等地球徹底毀滅),然後敘述2的副作用(例如引爆氫 彈)才能開始(再毀滅一次)。許多運算子等等也有規定副作用的順序, (篇幅關係全部省掉 xD)標準認為修改變數中的內容算副作用,可是很多 實際上在用的編譯器才不鳥這規定。也就是說, | int a = 0; | int b = 0; | a++; | b++; (假設實作用現在常見的記憶體充當虛擬機器的記憶體) 理論上要等 a 的新值儲存完才能開始對 b 動刀,但實際上很多編譯器 都不按標準實作,因為它們覺得修改 a 和 b 的內容不算「副作用」。(請 原諒我一直避開艱澀難懂的術語。)第一個重點和文章後面的附註,就是要 強調「編譯器其實沒按照標準做」這件事情。這其實是情由可原的,因為大 部分的情況下的確分辨不出來 a 和 b 到底誰先改,然後如果編譯器不想辦 法把最佳化的手段推到極限,還會被別人抱怨。(「穩定但比較慢」的實作 永遠不及「快速而表面上還行」的實作受歡迎... 直到哪天出了大問題才會 檢討吧,我猜。) 註:個人不同意直接說這兩個不算副作用,見文章最後面。 第二個重點是,未定義行為不算在副作用中。如果召喚外星人是未定義 行為,然後你寫: | 引爆核彈; | 召喚外星人; 有可能核彈都還沒引爆,外星人就出現,然後把地球瞬間變成黑洞。撇 開第一個問題(沒按照標準實作)不談,在第二個問題上標準和編譯器可謂 有志一同,一致認為召喚出外星人是程式設計師的責任。即使表面上看起來, 地球要先被炸一炸才變成黑洞,但這不是真實世界的樣子。口說無憑,網路 上那篇文章就舉了一個例子惡整 clang. 就我測試的結果,把 printf 換成 fprintf (stderr, ... 也都可以重現它的結果。文章主要的意思是常常有人 用 printf 來偵測外星人在哪裡: | /* 好多程式碼 */ | | 發出 SOS 訊號; | | /* 又好多程式碼 */ | | 召喚外星人; 表面上看起來,如果我們在螢幕上沒有發現 SOS 訊號,就可以篤定外星 人在那行程式碼之前出現。這種推論是錯的。即使表面上外星人是後面才會 出現,它可以在你發出訊號前把地球變成黑洞。 註:個人覺得不必像文章討論算不算副作用,因為未定義行為會直接讓 整個程式都沒有定義,當然也不受上面什麼分號的約束了。技術上來說,編 譯器遇到有未定義行為的程式,隨便怎麼實作都無所謂。 希望看了我亂七八糟的譬喻後對你有幫助 xD -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.30.39 ※ 編輯: Favonia 來自: 140.112.30.39 (07/06 12:46) 編輯:2^N 應為 2^(N-1), 不好意思打錯了。 ※ 編輯: Favonia 來自: 140.112.30.39 (07/06 12:53)

07/06 13:17, , 1F
第一個問題是了解了,第二個問題是大概了解.
07/06 13:17, 1F

07/06 13:18, , 2F
問題看起來很蠢,但是卻是台灣大部分工程師常犯的錯誤喔
07/06 13:18, 2F

07/06 13:19, , 3F
功力還是不夠,喝.
07/06 13:19, 3F

07/06 13:20, , 4F
謝謝
07/06 13:20, 4F
稍微再補了一點 :Q

07/06 18:13, , 5F
推幽默的精彩解說!
07/06 18:13, 5F
※ 編輯: Favonia 來自: 140.112.30.39 (07/06 20:11)

07/06 21:13, , 6F
就可以篤定問題出在那行程式碼之前。這是錯的。...<-
07/06 21:13, 6F

07/06 21:13, , 7F
意思是不是召喚外星人先出現阿?
07/06 21:13, 7F
嗯,應該就是你的意思。標準允許外星人在隨便一個地方出現。我改一 下文章 xDDD ※ 編輯: Favonia 來自: 140.112.30.39 (07/06 21:17)

07/06 21:20, , 8F
這是錯的。外星人是無敵的<--這個可能要移除一下,不然
07/06 21:20, 8F

07/06 21:23, , 9F
句子不通.喔,我懂了那篇的講法了.謝謝你跟另一個熱心
07/06 21:23, 9F

07/06 21:24, , 10F
的人.
07/06 21:24, 10F
嗯改好了 xD 謝謝你的建議。 ※ 編輯: Favonia 來自: 140.112.30.39 (07/06 21:26)

07/06 21:41, , 11F
我才該道謝,因為英文不好,其實part3,只是有點感覺才來
07/06 21:41, 11F

07/06 21:42, , 12F
麻煩大家.因此才搞懂以前忽略的問題.<m.m>
07/06 21:42, 12F
※ 編輯: Favonia 來自: 140.112.30.39 (07/07 04:25)

07/07 13:25, , 13F
你到底是有多討厭地球啊XDDDD
07/07 13:25, 13F
不好意思下次改火星好了 xDD 編輯:重寫一些細節 xD ※ 編輯: Favonia 來自: 140.112.30.39 (07/07 19:56)

07/07 21:00, , 14F
= =恩,誰來告訴我,怎麼把標題改成已解決
07/07 21:00, 14F

07/07 21:05, , 15F
E 修改內文標題, T 修改列表標題。
07/07 21:05, 15F

07/07 21:05, , 16F
連按 hh, 呼叫覆蓋在 ptt 桌面上的 小天使
07/07 21:05, 16F

07/07 21:13, , 17F
謝謝,發卡
07/07 21:13, 17F

07/07 22:42, , 18F
推一下:)
07/07 22:42, 18F
編輯:補一下自己意見好了,因為這篇文章是按照網路上那篇文章打的, 我沒有完全同意 orz 我的意見標上黃色。不想被人當作我同意所有看法 xD ※ 編輯: Favonia 來自: 140.112.30.39 (07/08 06:16) 編輯:發現愚蠢的錯字 Orz ※ 編輯: Favonia 來自: 140.112.30.39 (07/08 06:42) ※ 編輯: Favonia 來自: 140.112.30.39 (07/08 06:42)

07/08 08:57, , 19F
=..=很完美了,可以結案了!
07/08 08:57, 19F

07/11 16:21, , 20F
補充一點好了-INT_MIN = INT_MIN,這才是作者想避免的
07/11 16:21, 20F
※ 編輯: Favonia 來自: 140.112.30.39 (07/23 22:22) 我想了好久天才想到要怎麼補一個很討厭的東西,修正我之前講的話: 標準雖然有一大堆規定,但是實際上那是針對虛擬機器定的。如果編譯器 不把真實記憶體直接當作虛擬機器的記憶體,則上述的最佳化可能都沒有關系。 引用這段標準: | An implementation might define a one-to-one correspondence | between abstract and actual semantics: at every sequence point, | the values of the actual objects would agree with those | specified by the abstract semantics. The keyword volatile would | then be redundant. 這個 blog 比較像是立基於上面這種實作來批評。它假設實體的記憶體和 虛擬機器的記憶體之間有某種簡單的對應。但其實標準接著說... | Alternatively, an implementation might perform various optimi- | zations within each translation unit, such that the actual | semantics would agree with the abstract semantics only when | making function calls across translation unit boundaries. In | such an implementation, at the time of each function entry and | function return where the calling function and the called | function are in different translation units, the values of all | externally linked objects and of all objects accessible via | pointers therein would agree with the abstract semantics. | Furthermore, at the time of each such function entry the values | of the parameters of the called function and of all objects | accessible via pointers therein would agree with the abstract | semantics. In this type of implementation, objects referred to | by interrupt service routines activated by the signal function | would require explicit specification of volatile storage, as | well as other implementation-defined restrictions. 所以實作有很大的彈性可以亂搞。實務上的問題(包括模糊的標準如何解 讀)可以參考這裡: http://www.gnu.org/software/autoconf/manual/html_node/Volatile-Objects.html ※ 編輯: Favonia 來自: 140.112.30.39 (07/24 22:10) ※ 編輯: Favonia 來自: 140.112.30.39 (07/30 10:31)
文章代碼(AID): #1E4-Y3cH (C_and_CPP)