有效的程式在C89,但不是在C99
在C99中是否引入或刪除了特徵/語義,這將使得在C89中編寫的定義良好的程式也是
>無效(即根據C99標準不再編譯)
>編譯,但具有不同的語義.
到目前為止,我的研究結果顯然是無效的程式:
> int int(C89§3.5.2)
>隱式函式宣告(C89§3.3.2.2)
>不返回從期望返回值的函式(C89§3.6.6.4)
>使用新的關鍵字作為識別符號(例如restrict,inline等)
> hacks涉及//,現在被視為評論.但是,在生產程式碼中幾乎從未遇到過.
微妙的變化,使相同的程式碼具有不同的語義:
>整數除法已經做了很好的定義,例如-3 / 2現在必須截斷為零(C99§6.5.5/ 6),而不是被實現定義(C89§3.3.5/ 6)
> strtod可以通過解析0x或0X來解析C99中的十六進位制數
我錯過了什麼?
在C89出版之前有很多方案被認為是有效的,有些人堅持認為這些方案從未有效. C89包括一個規則,要求只能使用該型別的指標,相關型別或字元型別訪問任何型別的物件.在C99釋出之前,該規則通常被解釋為僅適用於“已命名”物件(靜態或自動持續時間的變數,它們通過名稱直接訪問),並且僅在所涉及的物件沒有其地址的情況下在它被用作不同的指標型別之前立即使用.這種解釋是由若干因素引起的:
>標準的一個明確目標之一是適應現有的編譯器和程式所做的工作,而現有程式對於使用不同型別的指標訪問離散命名變數將是罕見的,除非變數的地址為在使用前立即採取指標式打法的很多其他用法頗為普遍.
>標準的基本原理包括作為唯一的例子,該函式接收一個原始型別的指標以編寫另一個原始型別的全域性變數,使得編譯器沒有特定的理由期望混疊.能夠將全域性變數儲存在暫存器中顯然是一個有用的優化,並且規則的所述目的是在編譯器沒有理由期望出現混疊的情況下允許這種優化.類似(int *)& foo = 23的非法構造沒有什麼可以幫助這樣的優化,因為程式碼使用foo的地址和取消引用它的事實應該使任何編譯器非常清楚,這並不是故意鈍化程式碼將修改foo.
>有很多種程式碼需要語義上使用儲存器位作為各種型別的能力,標準中沒有什麼表示規則旨在使程式設計師跳過環(例如通過使用memcpy)來實現本來可能已經被使用的語義特別是考慮到使用memcpy會阻止編譯器將整個變數儲存在指標訪問的暫存器中(從而打破了首先寫入規則的目的).
>如果結構型別V和W具有共同的初始序列,則U是包含兩者的任何聯合型別,p是識別U內的V的V *,則可以使用(W *)(U *)p來訪問那些共同的成員,將等同於(W *)p.除非編譯器可能顯示p不可能是指向包含W的某個聯合的成員的指標,否則將需要允許(W *)p訪問公共成員;簡單地將這種普通成員訪問視為合法更有幫助,無論U是否存在,或者U可能存在,而不是搜尋藉口否認它.
> C89規則中的任何內容都不清楚如何定義分配儲存區域的“型別”,或者如何儲存一種不再需要的型別的儲存可能被重新設計來儲存另一種型別的內容.
>跟蹤分配給命名變數的暫存器比跟蹤分配給其他指標異常的暫存器更容易,而通過指標最小化載入和儲存數量感興趣的程式碼通常會將事物複製到命名變數並在其上工作.
C99添加了明確適用於分配儲存的“有效型別”規則.有些人堅持認為這些僅僅是C89中已經存在的規則的“澄清”,但由於上述原因,我覺得這一觀點是站不住腳的.有時候,編譯器不將別名規則應用於未命名物件的唯一原因是#5和#6,但反對#1-#4同樣重要(並繼續適用於C99,就像C89一樣).然而,由於C99添加了有效的型別規則,因此許多被C89規則的大多數常見解釋視為合法的構造是顯然被禁止的.
http://stackoverflow.com/questions/36703033/valid-programs-in-c89-but-not-in-c99