2017-07-18 93 views
4

我看了this很好太寬了問題,遇到一些UB我以前都不知道。有沒有規則可以發現UB?

UB我不時看到的主要原因是在兩個序列點之間改變兩次變量。諸如:x = x++z = y++ + ++y;。讀取在兩個序列點之間兩次更改變量是UB幫助我瞭解這些情況下的根本原因。

但是,像負位移位這樣的事情呢? (int x = 8 << -1)有沒有一個規則可以解釋或者我應該記住這是一個獨特的UB可能性?

我看了here和部分整數溢出我發現帶負片的位移是寫的,但我不明白他們爲什麼相關。當int被移位太多時,會引起溢出,但IMO發生負移位僅僅是UB,並且問題不在於「超出邊緣」的位...

也看了這裏,沒有回答我的問題:

對每個操作數執行整數升級。結果的類型是提升的左操作數的類型。如果右操作數的值爲負或者大於或等於提升的左操作數的寬度,則行爲未定義

所以我的問題是:

  1. 具體而言,位移位與底片考慮整數溢出,如果是這樣,爲什麼?
  2. 如果不是,它是更大現象的一部分嗎?
  3. 是否存在(其他)不能歸類於一個基本原因下的獨特個案?
+3

負移位未定義,移位過長(對於N位整數類型移位N位或更多位)也是如此。該標準如此說。你必須知道它是這樣說的。是的,有很多情況,將它們分組會很棘手。 C11標準的附錄J.2在第557-571頁上記錄了未定義的行爲(每個結束頁面只有幾行,因此它多於14頁)。定期閱讀以瞭解未定義的內容。沒有;我還沒有記住它。 –

+0

@JonathanLeffler這是一個令人印象深刻的名單,thx!儘管我希望有更容易記住的東西:) – CIsForCookies

+1

回答你的部分 - *我希望更容易記住的東西*我的一般經驗法則是 - 任何似乎改變不同實現(目標,平臺等)行爲的東西是一個紅旗「點」UB。然後我確認清單。這很有意義,因爲標準是在抽象機器上定義的,而不是任何實現。因此觸摸實現的方面必須保持未定義。警惕實施定義的行爲。 –

回答

1

具體來說,是位移與底片考慮整數溢出和如果是這樣,爲什麼?

這不是,因爲任何數量的移位0都不會溢出,但它仍然是未定義的行爲,將值0移動一個負值。 (我假設你可以認爲它是整數溢出,如果你首先將移位量重新解釋爲一個無符號整數,此時它會很大並且肯定超出允許的範圍,並且如果解釋爲如果移位的值不爲零,則乘以2的乘方肯定會溢出)。

簡而言之,負偏移產生未定義的行爲,因爲語言標準說明了這一點。

如果不是,它是一個更大的現象的一部分?

John Regehr在a blog post中給出了UB的一些大類。按無效金額移位在「其他UB」類別中...

是否存在(其他)不能歸入一個潛在原因下的獨特個案?

是的,看到上面的帖子。其中(這些直接從博客文章中解除):

  • 減去指向未指向同一數組對象或超出同一數組對象的指針(6.5.6)。
  • 對象的存儲值不是由允許類型的左值訪問的(6.5)
  • 非空源文件不會以換行符結尾,換行符不是以反斜槓字符開頭或結尾部分預處理令牌或註釋(5.1.1.2)

您可能會以某種方式對這些和其他示例進行分類,但這取決於您如何執行此操作。

特別是,上面的最後一個例子(關於源文件沒有以換行符結尾)顯示了一些規則是多麼的隨意。

+0

現在你已經有了這個內容的答案,我提供了從我的答案中刪除你不錯的鏈接,爲了不承擔你的功勞。你想要我嗎? – Yunnosch

+0

@Yunnosch不,沒關係,但謝謝你的提問。 – davmac

1

(編譯自評的答案,包括我)

一個很好的出發點,發現實際的未定義行爲(UB)是這些引用由Jonathan Leffler

是的,有很多情況下,分組將是棘手的。 C11標準的附錄J.2在第557-571頁上記錄了未定義的行爲(每個結束頁面只有幾行,因此它多於14頁)。

引用一篇相關文章,其中介紹了UB的類型,用於識別的工具和包含UB的列表;長(作者的意圖),完成(davmac的cortesy):
的東西 「memorizable」 https://blog.regehr.org/archives/1520

兩種方法:

  1. by Ajay Brahmakshatriya,重點不可避免的平臺依賴性:

    我的一般經驗法則是 - 任何似乎會改變不同實現(目標,平臺等)行爲的行爲都是「現貨」的紅旗UB

  2. by Yunnosch,重點問題,以平衡標準化和優化:

    如果它很可能是努力使硬件供應商同意這,或否則很難明確界定,並允許一定空間優化的實現,那麼它可能是UB。

可悲的是,所有這些 「規則」 並不容易申請。 檢查實際標準是不方便的。這兩個經驗法則是基於相當一些必需的經驗;你或者需要設計一些編譯器和/或處理器,或者由於它們之間的差異而遭受了很大的影響。

所以實際的答案「有沒有一種簡單的方法來發現UB?」 可能是簡單的「號」

+0

我會考慮將此標記爲社區答案,因爲這很大一部分不是你的話。如果你這樣做,請刪除頂部的括號註釋。 –

0

x<<y與本案y負,也有一些平臺將處理類似z=x<<y與微等同於:

unsigned temp = x; 
unsigned count=y; 
while(count--) 
    temp<<=1; 
z=temp; 

如果y是負的,這個循環可能會遇到很長的時間;如果它是在微碼級別處理的(我認爲一些Transputer芯片就是這種方式),它可能在幾分鐘內禁用中斷,這可能會干擾系統的其他方面。

在大多數平臺上它會花費什麼,做作的場景之外,對於編譯器,以保證x<<y不會有針對xy以後產生一個可能,無意義值的任何值的任何副作用;事實上,編譯器可以更輕鬆地生成沒有副作用的代碼,而不需要執行其他任何操作。不幸的是,一些編譯器作者認爲,他們應該尋找「巧妙」的方式來利用「不能」消極的事實,引發任意壞的後果,而不考慮其實際上是否有用,或許錯誤地認爲「聰明「和」愚蠢「是反義詞。

+0

還有一些平臺,其中只有足夠的'y'位連接到移位單元以支持有意義的移位範圍(即,如果'y'被屏蔽爲'x'位的大小)。 –

+0

@TobySpeight:通常情況下,大的y值的'x << y'或者表現爲x <<(y-1)<< 1'或者'x <<(y&numbits)',但是重要的是除了產生可能無意義的價值之外,它們都不會產生副作用。 – supercat