1. 程式人生 > >c++中的編譯、連結和執行

c++中的編譯、連結和執行

1.編譯與連結的區別:

預處理:處理巨集定義指令#define 、標頭檔案#include等

#include<filename> ,尖括號表示系統提供的標頭檔案,直接去系統目錄查詢;

#include“animal.h”,雙引號表示自己編寫的標頭檔案,先在工程目錄裡面查詢,找不到再到系統目錄查詢。

預編譯程式所完成的基本上是對源程式的“替代”工作。經過此種替代,生成一個沒有巨集定義、沒有條件編譯指令、沒有特殊符號的輸出檔案。這個檔案的含義同沒有經過預處理的原始檔是相同的,但內容有所不同,經過預編譯後產生完整的原始檔,將此檔案作為編譯程式的輸入而被翻譯成為機器指令

編譯:將高階語言(如c++)轉換為機器語言(二進位制)

編譯過程中,每個原始檔進行單獨編譯

編譯技巧:編譯的作用是對源程式進行詞法檢查、語法檢查和中間程式碼生成。編譯時對檔案中的全部內容進行檢查,如果有語法錯誤,編譯結束後會顯示出所有的編譯出錯資訊,開發人員可以根據錯誤提示修改程式。對於新寫的一個保護多個檔案的工程,一開始採用原始檔分別編譯,這樣容易發現每個原始檔的自身錯誤,限定了錯誤的範圍,如果一開始就採用全部編譯,多個原始檔可能會產生許多錯誤,無形中增加了開發難度。如果每個原始檔都通過了編譯,再將所有檔案進行編譯。對原始檔分別編譯對於除錯,糾錯是一種很好的方法。

連結:主要解決編譯生成的若干目標模組間相互引用的問題。包括他們所需要的庫函式、地址和空間分配、符號解析、重定位等幾個步驟。

在編譯階段生成目標檔案時會暫時擱置外部引用,而這些引用是在連結時確定的。

連結器連結時會根據符號名稱去相應模組中尋找對應的符號,待符號確定之後,連結器會重寫之前那些未確定的符號地址,這個過程就是重定位。 

連結分類:靜態連結、載入時動態連結、執行時動態連結

靜態連結:在這種連結方式下,函式的程式碼將從其所在地靜態連結庫中被拷貝到最終的可執行程式中。這樣該程式在被執行時這些程式碼將被裝入到該程序的虛擬地址空間中。靜態連結庫實際上是一個目標檔案的集合,其中的每個檔案含有庫中的一個或者一組相關函式的程式碼(即:在程式執行前,預先把函式程式碼靜態拷貝進可執行程式中

載入時動態連結:使用者源程式經編譯後所得到的目標模組,是在裝入記憶體時,邊裝入邊連結的.即在裝入一個目標模組時,若發生一個外部模組呼叫,將引起裝入程式去找出相應的外部目標模組,並將它裝入記憶體,還要修改目標模組中的相對地址

執行時動態連結:可將某些目標模組的連結,推遲到執行時才進行。即在執行過程中,若發現一個被呼叫模組尚未裝入記憶體時

,由OS去找到該模組,將它裝入記憶體,並把它連線到呼叫者模組上。

例如:在C++中,把原始檔(.cpp)翻譯成機器語言檔案(.obj)——編譯過程

                                把許多.obj檔案合成執行檔案.exe

1.在c++程式中呼叫被c編譯後的函式,為什麼要加extern “C”

 c++是面向物件變成,支援函式過載,C語言是面向過程的程式語言,不支援過載,所以函式被c++編譯後在庫中的名字會和c語言不同。

例子:宣告C語言函式:float f (int a, char b), c++編譯器就會把名字程式設計類似_f_int_char之類的東西以支援函式過載,然而C語言編譯器的庫一般不執行該轉換,所以它的內部名字為_f,這樣連結器無法解釋c++對函式f()的呼叫。

解決辦法:extern "C" (替代連結說明符)。extern是c、c++語言中標明函式和全域性變數作用範圍(可見性)的關鍵字。該關鍵字告訴編譯器,其宣告的函式和變數可以在模組或其他模組中使用,extern後面跟著一個字串來指定想宣告的函式的連續型別,後面是函式的宣告。

extern “C” float f(int a, char b);

該語句旨在告訴編譯器f()是c連結的,這樣c++就不會轉換函式名。標準的連結型別指定符有“C”和“c++”兩種,如果有一組替代連結的宣告,可以放在花括號裡面:

extern "C"

{

       float f(int a, char b);

       ...//other funcs

}

c++編譯器已經對c標準庫的標頭檔案做了extern “C”處理,所以可以使用#include直接引用這些標頭檔案。

參考文獻:《面試筆試程式設計師寶典 第二版》何昊等,機械工業出版社。