2016-03-15 38 views
6

在此基礎上很好的博客文章,The Strict Aliasing Situation is Pretty Bad,我已經放置一段代碼在線爲您進行測試:C給出基於優化級別(如新)不同的輸出

http://cpp.sh/9kht(輸出之間的變化 - O0和-02)

#include <stdio.h> 

long foo(int *x, long *y) { 
    *x = 0; 
    *y = 1; 
    return *x; 
} 

int main(void) { 
    long l; 
    printf("%ld\n", foo((int *)&l, &l)); 
} 
  • 是否有某種這裏未定義行爲?

  • 當我們選擇-O2水平時,內部發生了什麼?

+1

這實際上是違反類型別名規則的一個很好的例子。我會把它作爲我的規範副本。 – SergeyA

回答

12
  1. 是的,這個計劃已經未定義因爲基於類型的別名規則,可以概括爲「你無法通過類型的指針訪問與A類聲明的內存位置的行爲, B,,除了當B是指向字符類型的指針(例如unsigned char *)。「這是一個近似值,但對於大多數目的而言它已經足夠接近。請注意,當A是指向字符類型的指針,B可能而不是是別的 - 是的,這意味着通過uint32_t*訪問字節緩衝區「一次四個」的常見習慣是未定義的行爲(博客帖子也涉及到這一點)。

  2. 當編譯foo時,編譯器假定xy可能不指向同一個對象。由此推斷,通過*y的寫入不能改變*x的值,並且它可以僅返回已知值*x,0而不從內存中重新讀取它。它只在打開優化時纔會執行此操作,因爲要跟蹤每個指針可以指向哪些內容並且不能指向的開銷很大(因此編譯速度較慢)。

    請注意,這是一個「惡魔飛出你的鼻子」的局面:編譯有權使生成的代碼foo開始與

    cmp rx, ry 
    beq __crash_the_program 
    ... 
    

    (等UBSan一個工具可能做到這一點)

+0

非常好的回答@zwol,謝謝。可能想要擴展一些基於類型的別名規則? (比如他們在spec中的位置,其他例子等) – Dave5545

+2

其實,char,signed char和unsigned char是允許訪問任意類型的三種不同類型。 – EOF

+0

@ Dave5545當我在裝有我的C99副本的計算機上時,我會這樣做。 – zwol

1

換言之,代碼(int *)&l表示將指針視爲指向int的指針。它不會轉換任何東西。因此,(int *)告訴編譯器允許您將long *傳遞給期望int *的函數。你在撒謊。在裏面,foo希望x是一個指向int的指針,但它不是。內存佈局不是它應該的。如你所見,結果是不可預測的。

另一方面,我不會使用l(ell)作爲變量名稱。它很容易與1(一)混淆。例如,這是什麼?

int x = l;