C++ 程式設計
連結器是一個生成可執行檔案的程式。連結器解決連結問題,例如在某個翻譯單元中定義並在其他翻譯單元中需要的符號或識別符號的使用。在單個翻譯單元之外需要使用的符號或識別符號具有外部連結。簡而言之,連結器的任務是透過找出哪個其他物件定義了有問題的符號來解析對未定義符號的引用,並將佔位符替換為該符號的地址。當然,這個過程比這更復雜;但基本思想適用。
連結器可以從稱為庫的集合中獲取物件。根據庫(系統或語言或外部庫)和傳遞的選項,它們可能只包含從其他目標檔案或庫中引用的符號。存在用於各種目的的庫,並且通常預設情況下會連結一個或多個系統庫。我們將在本書的庫部分中更深入地瞭解庫。
將編譯器生成的程式碼檔案與建立可執行程式(或庫)所需的庫連線或組合的過程稱為連結。連結是指將程式從多個翻譯單元構建起來的方式。
C++ 程式可以與用其他語言編寫的程式進行編譯和連結,例如 C、Fortran、組合語言和 Pascal。
- 適當的編譯器分別編譯每個模組。C++ 編譯器將每個“.cpp”檔案編譯成一個“.o”檔案,彙編器將每個“.asm”檔案彙編成一個“.o”檔案,Pascal 編譯器將每個“.pas”檔案編譯成一個“.o”檔案,等等。
- 連結器在單獨的步驟中將所有“.o”檔案連結在一起,建立最終的可執行檔案。
每個函式要麼具有外部連結,要麼具有內部連結。
具有內部連結的函式僅在一個翻譯單元內可見。當編譯器編譯具有內部連結的函式時,編譯器會將該函式的機器程式碼寫入某個地址,並將該地址放入對該函式的所有呼叫中(這些呼叫都在該翻譯單元中),但會從“.o”檔案中刪除對該函式的所有提及。如果對某個明顯具有內部連結但似乎未在此翻譯單元中定義的函式進行了一些呼叫,則編譯器可以立即告知程式設計師有關該問題(錯誤)。如果存在一些具有內部連結但從未被呼叫的函式,則編譯器可以進行“死程式碼消除”,並將其從“.o”檔案中排除。
連結器永遠不會聽說過那些具有內部連結的函式,因此它對它們一無所知。
用外部連結宣告的函式在多個翻譯單元內可見。當編譯器在某個翻譯單元中編譯對該函式的呼叫時,它不知道該函式在哪裡,因此它會在對該函式的所有呼叫中留下一個佔位符,以及“.o”檔案中的指令,用具有該名稱的函式的地址替換該佔位符。如果該函式從未定義,則編譯器不可能知道這一點,因此程式設計師直到很久以後才會收到有關該問題(錯誤)的警告。
當編譯器編譯(定義)具有外部連結的函式(在其他翻譯單元中)時,編譯器會將該函式的機器程式碼寫入某個地址,並將該地址和該函式的名稱放入“.o”檔案中,供連結器查詢。編譯器假設該函式將從其他翻譯單元(其他“.o”檔案)呼叫,並且必須將該函式保留在此“.o”檔案中,即使最終發現該函式從未從任何翻譯單元呼叫。
大多數程式碼約定規定標頭檔案只包含宣告,不包含定義。大多數程式碼約定規定實現檔案(“.cpp”檔案)只包含定義和區域性宣告,不包含外部宣告。
這會導致“extern”關鍵字只用在標頭檔案中,從未用在實現檔案中。這會導致內部連結只在實現檔案中指示,從未在標頭檔案中指示。這會導致“static”關鍵字只用在實現檔案中,從未用在標頭檔案中,除非“static”用在標頭檔案中的類定義內部,它表示的含義與內部連結不同。
我們將在本書的檔案組織部分中更詳細地討論標頭檔案和實現檔案。
static 關鍵字可以用四種不同的方式使用
- 內部連結
當在自由函式、全域性變數或全域性常量上使用時,它指定內部連結(與extern不同,後者指定外部連結)。內部連結將對資料或函式的訪問限制在當前檔案中。
不在任何函式或類內部使用示例
static int apples = 15;
- 定義一個名為apples的“靜態全域性”變數,初始值為 15,僅從此翻譯單元可見。
static int bananas;
- 定義一個名為bananas,初始值為 0,僅從此翻譯單元可見。
int g_fruit;
- 定義一個名為g_fruit的全域性變數,初始值為 0,從每個翻譯單元可見。此類變數通常不被認為是良好的風格。
static const int muffins_per_pan=12;
- 定義一個名為muffins_per_pan的變數,僅在此翻譯單元可見。static 關鍵字在此處是多餘的。
const int hours_per_day=24;
- 定義一個名為hours_per_day的變數,僅在此翻譯單元可見。(這與).
static const int hours_per_day=24;
static void f();
- 宣告存在一個函式f不帶引數且沒有返回值,在該翻譯單元中定義。此類前向宣告通常在定義相互遞迴函式時使用。
static void f(){;}
- 定義函式f()如上所述宣告。此函式只能從該翻譯單元中的其他函式和成員呼叫;它對其他翻譯單元不可見。
C++ 標準庫中的所有實體都具有外部連結。
extern 關鍵字告訴編譯器某個變數是在另一個源模組(在當前作用域之外)中定義的。然後連結器找到此實際宣告,並將extern 變數設定為指向正確的位置。由extern 語句描述的變數將不會為其分配任何空間,因為它們應該在其他地方正確定義。如果某個變數被宣告為 extern,而連結器找不到它的實際宣告,它將丟擲“未解析的外部符號”錯誤。
示例
extern int i;
- 宣告存在一個名為i 的 int 型別變數,在程式中的某個地方定義。
extern int j = 0;
- 定義一個變數j具有外部連結;
extern關鍵字在此處是多餘的。
extern void f();
- 宣告存在一個函式f不接受任何引數,並且在程式中的某個地方沒有定義返回值;
extern是多餘的,但有時被認為是好的風格。
extern void f() {;}
- 定義函式f()在上面宣告;同樣,
extern關鍵字在這裡在技術上是多餘的,因為外部連結是預設的。
extern const int k = 1;
- 定義一個常量int k值為1並且具有外部連結;extern是必需的,因為const變數預設情況下具有內部連結。
extern語句經常用於允許資料跨越多個檔案的作用域。
當應用於函式宣告時,額外的“C”或“C++”字串文字將在使用相反語言編譯時更改名稱修飾。也就是說,extern "C" int plain_c_func(int param);允許 C++ 程式碼執行 C 庫函式 plain_c_func。
