2014-12-29 62 views
5

說我有一個編譯單元file1.c中,該聲明文件範圍的變量,像這樣:當定義不是const時,將extern global作爲const是否有效?

int my_variable = 12; 

然後,在另一個編譯單元file2.c中,我創建該extern聲明變量,但其聲明爲const

extern const int my_variable; 

這將彙編和GCC做工精細,用-Wall -Wextra -ansi -pedantic。但是,C89標準規定對於兩種符合要求的合格類型,兩者都應具有兼容類型的相同版本。將const添加到聲明中會增加一個限制,而不是避免一個。這是安全有效的C嗎?用頭文件設置這個最好的做法是什麼?

+2

如果放棄'const' quialifier,編譯器可能會抱怨,如果它是另一種方式。但我不太確定。 –

+2

我很確定[C - 通過const聲明訪問非const](http://stackoverflow.com/q/8051969/1708801)涵蓋了這種情況。 –

+0

可能值得指定您所指的確切的C標準版本。 – Clifford

回答

5

由於聲明不匹配,顯然未定義。如您所述,const intint是不兼容的類型。僅當診斷出現在相同範圍內時才需要診斷。

這不是在實踐中既安全,考慮

$ cat test1.c 
#include <stdio.h> 

extern const int n; 
void foo(void); 

int main(void) { 
    printf("%d\n", n); 
    foo(); 
    printf("%d\n", n); 
} 
$ cat test2.c 
int n; 
void foo(void) { ++n; } 
$ gcc -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
1 
$ gcc -O1 -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
0 

gcc的假設進行優化後nfoo()改變,因爲它可能會承擔n的定義是一個兼容型的,從而const

很可能你會得到預期的行爲volatile -qualifying n in test1.c,但就C標準而言,這仍然是未定義的。

我能想到的,以防止用戶意外修改n最好的辦法是一個指針申報const,沿

int my_real_variable; 
const int *const my_variable = &my_real_variable; 

或者是一些宏觀

#define my_variable (*(const int *)&my_variable) 

隨着C99的東西, my_real_variable可以通過複合文字避免:

const int *const my_variable_ptr = &(int){ 12 }; 

在這裏丟棄const是合法的(因爲int對象本身不是const),但是需要強制轉換,以防止意外修改。

+0

這很好地回答了這個問題;這個例子就是我正在尋找的。 – Quackmatic

4

在這種情況下,定義和聲明出現在單獨的翻譯單元中,所以編譯器不能執行任何類型或限定符檢查。這些符號由鏈接器解析,在這種情況下,似乎鏈接器不執行此限定符匹配。

如果定義和聲明出現在同一個翻譯單元中;例如,如果你把extern聲明放在一個頭文件中並且包含了那麼它在file1.c,那麼我想象一下編譯器會對抱怨。通過將它們放置在單獨的翻譯單元中,編譯器從不會看到兩者,因此無法執行檢查。

+0

我從這裏假設鏈接器在這種情況下的行爲是特定於實現的?在這種情況下,我所知道的是,C的名字裝飾不會注意'const'(例如C++) – Quackmatic

相關問題