2013-02-06 50 views
9

在優秀的博客文章What Every Programmer Should Know About Undefined Behavior,「違反類型規則」一節中說:C++標準在哪裏描述了對基元指針的轉換?

這是不確定的行爲施放一個int *爲float *和取消對它的引用(訪問「INT」就好像它是一個「浮動」)。 C要求這些類型的轉換通過memcpy進行:使用指針轉換不正確,導致未定義的行爲。對此的規則相當細緻,我不想在這裏詳細討論(char *有一個例外,向量具有特殊屬性,工會改變事物等)。

我想了解他們完全細緻的規則。他們在C++ 11規範中哪裏?否則,C規格(C90,C99,C11)?

在從this Stack Overflow question,N3485鏈接的C++ 11規範中,我正在尋找5.2.10「重新解析轉換」,但沒有看到char *或聯合的例外語言。所以這可能不是正確的地方。那麼合適的地方在哪裏?

+0

在C++中,你應該看看在不同類型的鑄件標準章節5.4,安全派生指針3.7.4.3,static_cast 5.2.9,reinterpret_cast 5.2.10 ... –

+1

偶然事件:昨天最新的STL [video](http://channel9.msdn.com/Series/C9-Lectures- Channel 9上的Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-Cpp-8-of-n)包含C++類型轉換中的段(包括對標準的引用) – TemplateRex

回答

4

你正在尋找的規則是在§3。10/10(在C++ 11):

如果一個程序試圖通過其它一個glvalue比以下類型的 行爲理解過程音響定義中的一個來訪問對象 的存儲值: - 的動態型的對象,

- 動態類型的對象的CV-合格音響編版本,

- 類似於一個類型(德音響定義在4.4)的動態類型的對象,

- 是一種類型對應於動態型的對象, 的符號或無符號類型 - 一種類型,是有符號或對應的動態對象的類型 的CV-合格音響編版本無符號類型,

- 一個聚集或聯合類型包括其元件或非靜態 數據成員(包括,遞歸地,一個元件或子聚集 或含有聯合的非靜態數據成員)之間的上述類型的一個,

- 一種類型,是一個(可能cv-quali fi ed)基類動態類型的對象,

- char或unsignedchar類型。

對於未定義的 行爲有不同的類型(或動機)。

在鑄造int*float*,然後 解引用它的情況下,很顯然,該標準不能定義 它,因爲會發生什麼取決於架構,並 的int的價值。在另一方面,引用的段落 是完全錯誤的—使用memcpy做轉換是 也是不確定的行爲,主要是由於同樣的原因。

一個動機未定義行爲的是 允許實現來定義它,以這樣的方式,使感 爲目標架構,如果這樣存在。這就是這樣的 一個案例。故意導致其失敗的編譯器是 有缺陷。當然,如果我們假設32位2的補碼 int和32位IEEE float,我們可以預計 ,int的某些值對應於捕獲NaN,這將導致程序 失敗。這是行爲是 未定義的原因的一部分;讓這種事情發生。但是如果我們熟悉 的低層硬件細節, 吧應該按預期工作,提供編譯器可以看到 的投。 如果不是這樣,這是編譯器的QoI問題,並且應該避免編譯器針對這些類型的工作。

作爲暗示以上,該特定情況下,事實上,在所有 案件涉及式雙關(寫入的 一個成員的結合,並從另一個讀取,例如),不要造成 一個問題,該標準尚未找到足夠的措辭。出現此問題的原因通常是,編譯器允許 假定指向不同類型的指針( 字符類型除外)不是別名; int*不能指向 與float*相同的對象。並證明兩個指針 不能別名對優化很重要。一個編譯器, 中斷代碼的指針演員或聯合是清晰可見的 剛剛壞了,即使標準說它是未定義的行爲。 一個編譯器可以將所有看到的代碼都分解爲兩個指針 到不相關的類型,這是可以理解的,即使在 標準說明行爲已被很好地定義的情況下也是如此。

使用memcpy通過使用兩個不同的 對象(它們不是別名)來避免此問題。它仍然遇到未定義的 行爲,因爲將int的位模式設置爲 a float,然後訪問浮動,沒有任何定義的 行爲。 (反之亦然。我知道至少有一臺機器,其中 複製的float位爲int可能導致 非法int值)

+0

非常感謝!關於memcpy,3.9節的第2段說:「對於一般可複製類型T的任何對象(不包括基類子對象),對象 是否保存類型T的有效值,組成該對象的基礎字節(1.7)對象可以被複制到char或unsigned char的數組 中。「 –

+0

@ MartinC.Martin是的。字符類型是特殊的,'char'和'unsigned char'都不允許有陷印值。同樣,當編譯器看到一個'char *'時,它必須假定指針可能會別名。 –

+0

我從來沒有見過一臺機器有非法的'int'值,哪一個是它? –

1

C++標準規定,如果某個行爲沒有被明確描述爲被定義,那麼它是隱含的未定義的。由於該標準沒有定義將int*轉換爲float*的行爲,因此它是隱式未定義的。

+0

它在哪裏說鑄造來自/來自char *的定義? –

0

上指針reinterpret_cast在的static_cast來定義上void指針

5.2.10重釋施放[expr.reinterpret.cast]>

7所述的對象的指針可以被顯式轉換爲一個不同類型的對象指針 。 70如果T1和T2是標準佈局 類型(3.9),並且T2的對齊要求是,則當「指向T1」的類型的prvalue v轉換爲類型「指向cv T2的指針」 時,結果爲static_cast<cv T2*>(static_cast<cv void*>(v))沒有比T1的那些更嚴格的 ,或者其中任何一種類型是無效的。轉換 類型的prvalue「指針T1」的類型「指針T2」(其​​中,T1和T2是 對象類型和其中T2的對準要求比T1的無 嚴格)和返回到其原始類型產生原始指針值 。未指定任何其他此類指針 轉換的結果。

5.2.9靜態鑄造[expr.static.cast]

13類型的prvalue「指針CV1空隙」可以被轉換爲一個 prvalue類型的「指針CV2 T「,其中T是對象類型,cv2與cv1具有相同的cv質量或更高的cv質量。 空指針值被轉換爲 目標類型的空指針值。類型的指針值到對象轉換爲 「指向cv空隙」和背部,可能與二FF erent CV-合格音響陽離子, 應具有其原始值。

+0

原始類型(char,int,float)是否也被視爲對象類型?或者是「對象」保留給類/結構體?在通俗用法中,人們經常區分基本類型和對象。 –

+0

1.8節似乎表明,原始類型實際上是對象。 –