DCL36-C. 不要聲明鏈接類型沖突的標(biāo)識(shí)符
鏈接可以使標(biāo)識(shí)符在不同的作用域中聲明捕虽,或者在同一個(gè)作用域中聲明多次(這些聲明指向同一個(gè)對(duì)象或函數(shù))。標(biāo)識(shí)符可以被分為外部鏈接校赤,內(nèi)部鏈接或無鏈接则涯。這三類鏈接具有以下特征 [Kirch-Prinz 2002]:
外部鏈接(External linkage): 一個(gè)具有外部鏈接屬性的標(biāo)識(shí)符在整個(gè)程序中(即所有屬于該程序的編譯單元和庫)都表示同一個(gè)對(duì)象或函數(shù)。鏈接器可以鏈接到該標(biāo)識(shí)符杨刨。當(dāng)另外一個(gè)同樣的標(biāo)識(shí)符被聲明時(shí)晤柄,鏈接器會(huì)將該標(biāo)識(shí)符 指定到同一個(gè)對(duì)象或函數(shù)。
內(nèi)部鏈接(Internal linkage): 一個(gè)具有內(nèi)部鏈接屬性的標(biāo)識(shí)符在指定的翻譯單元中表示通過一個(gè)對(duì)象或函數(shù)妖胀。鏈接器不知道內(nèi)部鏈接屬性標(biāo)識(shí)符的任何信息芥颈。這些標(biāo)識(shí)符只對(duì)翻譯單元內(nèi)部可見。
無鏈接(No linkage): 如果一個(gè)標(biāo)識(shí)符時(shí)無鏈接的赚抡,那么再聲明一個(gè)該標(biāo)識(shí)符都會(huì)聲明一個(gè)新的對(duì)象或類型.
根據(jù)C語言標(biāo)準(zhǔn)爬坑,6.2.2 [ISO/IEC 9899:2011], 鏈接被定義如下:
If the declaration of a file scope identifier for an object or a function contains the storage class specifier
static
, the identifier has internal linkage.For an identifier declared with the storage-class specifier
extern
in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier
extern
. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.The following identifiers have no linkage: an identifier declared to be anything other than an object or a function; an identifier declared to be a function parameter; a block scope identifier for an object declared without the storage-class specifier
extern
.
在某個(gè)翻譯單元中使用一個(gè)既是內(nèi)部又是外部鏈接類型的標(biāo)識(shí)符會(huì)導(dǎo)致 未定義行為(參見未定義行為 8)。一個(gè)翻譯單元包含源文件及其頭文件涂臣,以及所有通過預(yù)編譯指令#include
包含的源文件盾计。
下面這個(gè)表格標(biāo)識(shí)了在一個(gè)單獨(dú)的翻譯單元中聲明了兩次的對(duì)象的鏈接屬性。列表示第一次聲明赁遗,行表示再次聲明署辉。
不遵從規(guī)范的代碼示例
在這個(gè)不遵從規(guī)范的代碼示例中,i2
和i5
被定義為既是內(nèi)部又是外部鏈接屬性岩四。進(jìn)一步使用這兩個(gè)標(biāo)識(shí)符會(huì)導(dǎo)致未定義行為哭尝。
int i1 = 10; /* Definition, external linkage */
static int i2 = 20; /* Definition, internal linkage */
extern int i3 = 30; /* Definition, external linkage */
int i4; /* Tentative definition, external linkage */
static int i5; /* Tentative definition, internal linkage */
int i1; /* Valid tentative definition */
int i2; /* Undefined, linkage disagreement with previous */
int i3; /* Valid tentative definition */
int i4; /* Valid tentative definition */
int i5; /* Undefined, linkage disagreement with previous */
int main(void) {
/* ... */
return 0;
}
符合規(guī)范的解決方案
這個(gè)符合規(guī)范的方案不包含鏈接類型沖突的定義:
int i1 = 10; /* Definition, external linkage */
static int i2 = 20; /* Definition, internal linkage */
extern int i3 = 30; /* Definition, external linkage */
int i4; /* Tentative definition, external linkage */
static int i5; /* Tentative definition, internal linkage */
int main(void) {
/* ... */
return 0;
}