作為一名c/c++程式設計師,對於編譯鏈結的過程要了然於胸。首先大概介紹一下,編譯分為3步,首先對原始檔進行預處理,這個過程主要是處理一些#號定義的命令或語句(如巨集、#include、預編譯指令#ifdef等),生成*.i檔案;然後進行編譯,這個過程主要是進行詞法分析、語法分析和語義分析等,生成*.s的彙編檔案;最後進行彙編,這個過程比較簡單,就是將對應的彙編指令翻譯成機器指令,生成可重定位的二進位制目標檔案。以上就是編譯的過程,下面主要介紹兩種鏈結方式--靜態鏈結和動態鏈結。
靜態鏈結和動態鏈結兩者最大的區別就在於鏈結的時機不一樣,靜態鏈結是在形成可執行程式前,而動態鏈結的進行則是在程式執行時,下面來詳細介紹這兩種鏈結方式。
一、靜態鏈結
1.為什麼要進行靜態鏈結
在我們的實際開發中,不可能將所有**放在乙個原始檔中,所以會出現多個原始檔,而且多個原始檔之間不是獨立的,而會存在多種依賴關係,如乙個原始檔可能要呼叫另乙個原始檔中定義的函式,但是每個原始檔都是獨立編譯的,即每個*.c檔案會形成乙個*.o檔案,為了滿足前面說的依賴關係,則需要將這些原始檔產生的目標檔案進行鏈結,從而形成乙個可以執行的程式。這個鏈結的過程就是靜態鏈結
2.靜態鏈結的原理
由很多目標檔案進行鏈結形成的是靜態庫,反之靜態庫也可以簡單地看成是一組目標檔案的集合,即很多目標檔案經過壓縮打包後形成的乙個檔案,如下圖,使用ar命令的-a引數檢視靜態庫的組成:
這裡的*.o目標檔案在我前面的部落格《從編寫源**到程式在記憶體中執行的全過程解析》中已經講的很清楚了,不清楚的可以看一下。
以下面這個圖來簡單說明一下從靜態鏈結到可執行檔案的過程,根據在原始檔中包含的標頭檔案和程式中使用到的庫函式,如stdio.h中定義的printf()函式,在libc.a中找到目標檔案printf.o(這裡暫且不考慮printf()函式的依賴關係),然後將這個目標檔案和我們hello.o這個檔案進行鏈結形成我們的可執行檔案。
這裡有乙個小問題,就是從上面的圖中可以看到靜態執行庫裡面的乙個目標檔案只包含乙個函式,如libc.a裡面的printf.o只有printf()函式,strlen.o裡面只有strlen()函式。
我們知道,鏈結器在鏈結靜態鏈結庫的時候是以目標檔案為單位的。比如我們引用了靜態庫中的printf()函式,那麼鏈結器就會把庫中包含printf()函式的那個目標檔案鏈結進來,如果很多函式都放在乙個目標檔案中,很可能很多沒用的函式都被一起鏈結進了輸出結果中。由於執行庫有成百上千個函式,數量非常龐大,每個函式獨立地放在乙個目標檔案中可以儘量減少空間的浪費,那些沒有被用到的目標檔案就不要鏈結到最終的輸出檔案中。
3.靜態鏈結的優缺點
靜態鏈結的缺點很明顯,一是浪費空間,因為每個可執行程式中對所有需要的目標檔案都要有乙份副本,所以如果多個程式對同乙個目標檔案都有依賴,如多個程式中都呼叫了printf()函式,則這多個程式中都含有printf.o,所以同乙個目標檔案都在記憶體存在多個副本;另一方面就是更新比較困難,因為每當庫函式的**修改了,這個時候就需要重新進行編譯鏈結形成可執行程式。但是靜態鏈結的優點就是,在可執行程式中已經具備了所有執行程式所需要的任何東西,在執行的時候執行速度快。
問題:二、動態鏈結
1.為什麼會出現動態鏈結
動態鏈結出現的原因就是為了解決靜態鏈結中提到的兩個問題,一方面是空間浪費,另外一方面是更新困難。下面介紹一下如何解決這兩個問題。
2.動態鏈結的原理
動態鏈結的基本思想是把程式按照模組拆分成各個相對獨立部分,在程式執行時才將它們鏈結在一起形成乙個完整的程式,而不是像靜態鏈結一樣把所有程式模組都鏈結成乙個單獨的可執行檔案。下面簡單介紹動態鏈結的過程:
3.動態鏈結的優缺點
動態鏈結的優點顯而易見,就是即使需要每個程式都依賴同乙個庫,但是該庫不會像靜態鏈結那樣在記憶體中存在多分,副本,而是這多個程式在執行時共享同乙份副本;另乙個優點是,更新也比較方便,更新時只需要替換原來的目標檔案,而無需將所有的程式再重新鏈結一遍。當程式下一次執行時,新版本的目標檔案會被自動載入到記憶體並且鏈結起來,程式就完成了公升級的目標。但是動態鏈結也是有缺點的,因為把鏈結推遲到了程式執行時,所以每次執行程式都需要進行鏈結,所以效能會有一定損失。
據估算,動態鏈結和靜態鏈結相比,效能損失大約在5%以下。經過實踐證明,這點效能損失用來換區程式在空間上的節省和程式構建和公升級時的靈活性是值得的。
動態鏈結和靜態鏈結
在類的習慣性用法中,我們提到 對於類中 量較多的成員函式,僅僅在標頭檔案的類中宣告成員和函式用法,而將方法封裝起來。對於 量較大的成員函式來說,直接把 放在類中定義存在兩個問題 一是使用起來很不方便,二是破壞了資訊隱藏機制。那麼為什麼說將 放在類中定義會破壞資訊隱藏機制呢?實際上,對於變數和函式,一...
動態鏈結和靜態鏈結
庫從本質上來說是一種可執行的二進位制檔案,可以被載入到記憶體中執行,而根據鏈結時期的不同,庫又可以分為靜態庫和動態庫。靜態鏈結就是在編譯時將多個目標檔案組合在一起形成乙個可執行檔案的過程。1.空間與位址的分配過程中,首先會掃瞄所有的輸入檔案,獲得他們的各個段的長度,屬性和位置,然後將輸入檔案中所有的...
動態鏈結和靜態鏈結的區別
動態鏈結和靜態鏈結的區別 一 分別編譯與鏈結 linking 大多數高階語言都支援分別編譯,程式設計師可以顯式地把程式劃分為獨立的模組或檔案,然後每個獨立部分分別編譯。在編譯之後,由鏈結器把這些獨立的片段 稱為編譯單元 粘接到一起 想想這樣做有什麼好處?在c c 中,這些獨立的編譯單元包括obj檔案...