C 內鏈結 外鏈結

2022-08-19 05:18:15 字數 3766 閱讀 9651

編譯的時候(假如編譯器是vs),是以原始檔cpp檔案為單位,編譯成乙個個的obj檔案,然後再通過鏈結器把不同的obj檔案鏈結起來。如果一些變數或函式的定義是內連線的話,鏈結器鏈結的時候就不會拿它們去與obj比較看有沒有重複定義,乙個原始檔中即使是extern宣告的變數或函式也不能使用另外乙個原始檔中的內連線的變數或函式。而如果是外連線的話則需要在不同的obj中比較是否有重定義。除了做這樣的檢查外,鏈結器還會檢視通過extern修飾的變數或函式宣告在其他obj中的定義。綜上定義如下:

內部連線:如果乙個名稱對於它的編譯單元(經過預處理後的cpp檔案)來說是區域性的,並且在連線的時候不可能與其它編譯單元中的同樣的名稱相衝突,則這個名稱具有內部連線.即具有內部連線的名稱不會被帶到目標檔案中。

外部連線:在乙個多檔案程式中,如果乙個名稱在連線時可以和其他編譯單元互動,那麼這個名稱就具有外部連線。即具有外部連線的名稱會引入到目標檔案中,由連線程式進行處理。這種符號在整個程式中必須是唯一的。

以下情況是內連線:

1.所有的宣告

2.命名空間(包括全域性命名空間)中的靜態自由(非類的成員函式,也非友元函式)函式、靜態友元函式、靜態變數的定義

3.enum定義

4.inline函式定義(包括自由函式和非自由函式)

5.類的定義

6.命名空間中const常量定義

7.union的定義

以下情況是外連線:

1.類非inline函式總是外連線。包括類成員函式和類靜態成員函式

2.類靜態成員變數總是外連線。

3.命名空間(包括全域性命名空間)中非靜態自由函式、非靜態友元函式及非靜態變數

下面舉例說明:

1.宣告、enum定義、union定義是內連線

所有的宣告、enum定義及union定義在編譯後不會產生連線符號,也就是在不同編譯單元中有相同名稱的宣告及enum、union定義並不會在連線時發生發現多個符號的錯誤。

//

main.cpp

typedef

int int; //

typedef 宣告,內連線

enum color; //

enum定義,內連線

union x          

//union定義,內連線

;int main(void)//

a.cpp

typedef

int int; //

在a.cpp中重宣告乙個int型別別名,在連線時不會發生錯誤

enum color; //

在a.cpp中重定義了乙個enum color,在連線時不會發生錯誤

const int i =blue; //

const常量定義,內連線

union x           

//union定義,內連線

;

2.命名空間中靜態自由函式、靜態友元函式、靜態變數、const常量定義是內連線

//

main.cpp

namespace

test

//命名空間靜態函式定義,內連線

}static

int i = 0; //

全域性靜態變數定義,內連線

static

int foo() //

全域性靜態函式定義,內連線

const

int k = 0; //

全域性const常量定義,內連線

int main(void)//

a.cpp

namespace

test

//命名空間函式定義,外連線

}int i = 0;   //

全域性變數定義,外連線

int k = 0;   //

全域性變數定義,外連線

int foo() //

全域性函式定義,外連線

在全域性命名空間中,main.cpp中定義了靜態變數i,常量k,及靜態自由函式foo等,這些都是內連線。如果你將這些變數或函式的static或是const修飾符去掉,在連線時就會出現multiply defined symbols錯誤,它們與a.cpp中的全域性變數、全域性函式發生衝突。

5.類定義總是內連線,而非inline類成員函式定義總是外連線,不論這個成員函式是靜態、虛擬還是一般成員函式,類靜態資料成員定義總是外連線。

類的定義有內部連線。如果不是,想象一下你在4個cpp檔案中include定義了類base的標頭檔案,在4個編譯單元中的類base都是外連線,在連線的時候就會出錯。看下面的例子:

//

main.cpp

class b //

類定義,內部連線

//類inline函式,內連線

};structd ;

int b::s_i = 0; //

類靜態資料成員定義,外連線

void d::foo() //

類成員函式定義,外連線

int main() //

main函式,全域性自由函式,外連線

classb;

struct

d;

在這個例子中,main.cpp與a.cpp中都有class b和class d的定義,但在編譯這兩個cpp檔案時並不發生link錯誤。  

1.類的非inline成員函式(一般,靜態,虛擬都是)總是外連線,這樣當你include了某個類的標頭檔案,使用這個類的函式時,就能連線到正確的類成員函式上,繼續以上面為例子,如果把a.cpp中的struct d改為

struct d                       //

類定義;

void d::foo()               //

類成員函式定義,外連線

這時main.cpp與a.cpp中的d::foo都有外部連線,在連線就會出現multiply defined symbols錯。

2.類靜態成員變數總是外連線。

如上例的b::s_i,這樣當你在main.cpp中定義了類靜態資料成員,其它編譯單元若使用了b::s_i,就會連線到main.cpp對應編譯單元的s_i。

4.inline函式定義(包括自由函式和非自由函式)

inline函式總有內部連線,不論這個函式是什麼函式

//

main.cpp

inline

int foo() //

inline全域性函式,內連線

class bar //

類定義,內連線

//inline 類靜態函式,內連線

int g(int i) //

inline 類成員函式,內連線

};class

base

;inline

int base::k() //

inline 類成員函式,內連線

int main(void

)

如果你的base類是定義在base.h中,而base的inline 函式是在base.cpp中定義的,那麼在main.cpp中include "base.h"編譯不會出現問題,但在連線時會找不到函式k,所以類的inline函式最好放到標頭檔案中,讓每乙個包含標頭檔案的cpp都能找到inline函式。

C 的內鏈結與外鏈結

1 編譯單元 在編譯器編譯 時,只會去編譯.cpp檔案,而所有的標頭檔案會在預編譯時全部拷貝到.cpp中去,之後編譯.cpp檔案,所以每個.cpp檔案是乙個編譯單元。2 宣告 宣告是將乙個名稱引入到乙個作用域中來,並且,除了類中的成員函式和成員變數的外,在同乙個作用域是可以重複宣告的。3 定義 定義...

Oracle 外鏈結 內鏈結 等

b 首先建立兩張表,初始化資料 b sql create table zk test 1 id int,name varchar 10 sql insert into zk test 1 values 1,aaa sql insert into zk test 1 values 2,bbb sql ...

My SQL的內連線,外鏈結查詢

1 內連線 只連線匹配的行。2 左外連線 包含左邊表的全部行,以及右邊表中所有匹配的行,無論右邊的表有沒有和左邊匹配的行,左邊的所有行都必須要顯示。3 右外連線 包含右邊表的全部行,以及左邊表中所有匹配的行,無論左邊的表有沒有和右邊匹配的行,右邊的所有 行都必須要顯示。例 表t book的圖書類別編...