2017-06-01 64 views
2

https://kukuruku.co/post/i-do-not-know-c/這個指針別名是如何工作的?

問題#7:小端系統上的兩個不同的編譯器

#include <stdio.h> 
void f(int *i, long *l) 
{ 
    printf("1. v=%ld\n", *l); /* (1) */ 
    *i = 11;     /* (2) */ 
    printf("2. v=%ld\n", *l); /* (3) */ 
} 
int main() 
{ 
    long a = 10; 
    f((int *) &a, &a); 
    printf("3. v=%ld\n", a); 
    return 0; 
} 

輸出是:

1. v=10 2. v=11 3. v=11 
1. v=10 2. v=10 3. v=11 

如何爲第二次的結果可能嗎?我並不完全明白通過引用嚴格別名來解釋結果的解釋。編譯器是否完全忽略了行(2)?

+2

這是未定義的行爲。 * Upd:*順便說一句,看着文章,它*是*關於未定義的行爲。你讀過嗎? –

+0

我的猜測是你的編譯器2(可能是什麼)可能是做了一些'不成熟的優化' – Zakir

+6

@Zakir編譯器不容易*過早*優化.... –

回答

6

引用Wikipedia

在C或C++,通過嚴格別名規則,指針的授權在函數 參數被假定爲,如果它們指向 根本不同的類型,除了不別名爲char *和void *,可能 別名爲任何其他類型。某些編譯器允許關閉嚴格的別名規則 ,以便任何指針參數可以別名爲指針參數。在這種情況下,編譯器必須假定通過這些指針進行的任何訪問都可以是別名。這可以防止一些 優化。

這就是規則被違反:

f((int *) &a, &a); 
    ^aliasing to different type (a is 'long') 
      ^passing the same variable with different type 

的問題是,假定嚴格別名規則,功能點到另一個位置的第一和第二參數,因爲它們是不同類型的。這就是爲什麼作者解釋:

因此,我們可以假設任何長期沒有改變

而在這裏:printf("2. v=%ld\n", *l); a long值被取消引用。

這就是爲什麼這部分(2)在兩個編譯器上都是未定義的行爲。

3

部分(2)不被忽略或由編譯器被省略,它被執行並的a在呼叫者的範圍值在某些系統有關的方式修改,但是編譯器可以假設內部功能f(),修改int通過指針i不修改指向llong,因此它可以重複使用第一個printf()的讀取值作爲第二個printf的參數。

第二個編譯器似乎生成的代碼會執行此操作,而第一個編譯器生成的代碼將重新讀取l指向的值。事實上,編譯器選項(如優化設置)可以更改此行爲,這與將此代碼描述爲具有未定義行爲的C標準一致。

+0

使用相同編譯器的不同編譯器或編譯器選項具有不同行爲的事實是*不是*描述代碼具有**未定義行爲**的正當理由。 – curiousguy

+0

@curiousguy:好點,回答更新。謝謝。 – chqrlie

0

由於*l的值不能在(1)和(3)之間改變,使得第二個結果成爲可能。 (嚴格的別名規則確保。)

因此,程序只需要計算一次其值,並且編譯器生成的代碼相當於

void f(int *i, long *l) 
{ 
    long l2 = *l; 
    printf("1. v=%ld\n", l2); /* (1) */ 
    *i = 11;     /* (2) */ 
    printf("2. v=%ld\n", l2); /* (3) */ 
} 
相關問題