靜態鏈接與動態鏈接

什麼是鏈接?

對於初學C語言的朋友,可能對鏈接這個概念有點陌生,這裡簡單介紹一下。我們的C代碼編譯生成可執行程序會經過如下過程:

靜態鏈接與動態鏈接


鏈接就是把目標文件與一些庫文件生成可執行文件的一個過程。關於更詳細的編譯過程,可查閱上一篇筆記:


靜態、動態鏈接?

1、什麼是靜態鏈接?

靜態鏈接是由鏈接器在鏈接時將庫的內容加入到可執行程序中的做法。鏈接器是一個獨立程序,將一個或多個庫或目標文件(先前由編譯器或彙編器生成)鏈接到一塊生成可執行程序。這裡的庫指的是靜態鏈接庫,Windows下以.lib為後綴,Linux下以.a為後綴。


2、什麼是動態鏈接?

動態鏈接(Dynamic Linking),把鏈接這個過程推遲到了運行時再進行,在可執行文件裝載時或運行時,由操作系統的裝載程序加載庫。這裡的庫指的是動態鏈接庫,Windows下以.dll為後綴,Linux下以.so為後綴。值得一提的是,在Windows下的動態鏈接也可以用到.lib為後綴的文件,但這裡的.lib文件叫做導入庫,是由.dll文件生成的。


3、靜態鏈接與動態鏈接的優缺點?

(1)靜態鏈接的優缺點:

優點:

  • 代碼裝載速度快,執行速度略比動態鏈接庫快;
  • 只需保證在開發者的計算機中有正確的.lib文件,在以二進制形式發佈程序時不需考慮在用戶的計算機上.lib文件是否存在及版本問題。


缺點:

  • 使用靜態鏈接生成的可執行文件體積較大,包含相同的公共代碼,造成浪費。


(2)動態鏈接的優缺點:

優點:

  • 生成的可執行文件較靜態鏈接生成的可執行文件小;
  • 適用於大規模的軟件開發,使開發過程獨立、耦合度小,便於不同開發者和開發組織之間進行開發和測試;
  • 不同編程語言編寫的程序只要按照函數調用約定就可以調用同一個DLL函數;
  • DLL文件與EXE文件獨立,只要輸出接口不變(即名稱、參數、返回值類型和調用約定不變),更換DLL文件不會對EXE文件造成任何影響,因而極大地提高了可維護性和可擴展性;


缺點:

  • 使用動態鏈接庫的應用程序不是自完備的,它依賴的DLL模塊也要存在,如果使用載入時動態鏈接,程序啟動時發現DLL不存在,系統將終止程序並給出錯誤信息;
  • 速度比靜態鏈接慢;


靜態、動態鏈接實驗(windows)

下面通過實驗來理解Windows下的靜態鏈接與動態鏈接的過程。Windows下的靜態鏈接、動態鏈接實驗網上較多的是使用一些IDE如Visual Studio等通過圖形界面來操作,這樣就會掩蓋了很多細節。

本篇筆記我們不使用IDE,而是以Windows平臺搭配MinGW來演示,以便於日後我們在Linux下操作時可以比較快地進行切換。我們先編寫如下代碼(共三個文件):

文件1(main.c):

<code>#include "test.h"

int main(void)
{
print_hello();
system("pause");
return 0;
}/<code>


文件2(test.c):

<code>#include "test.h" 


void print_hello(void)
{
printf("hello world\\n");
}/<code>


文件3(test.h):

<code>#ifndef __TEST_H
#define __TEST_H

#include <stdio.h>
#include <stdlib.h>

void print_hello(void);

#endif/<stdlib.h>/<stdio.h>/<code>

此時我們的代碼目錄如下:

靜態鏈接與動態鏈接


1、靜態鏈接實驗

進入我們的代碼路徑,輸入命令:

<code>gcc -c test.c main.c/<code>
靜態鏈接與動態鏈接


編譯、彙編指定的源文件(也就是編譯源文件),將每一個源文件編譯成對應的目標文件。此時文件夾下多出了test.o和main.o文件:

靜態鏈接與動態鏈接


接下來使用ar工具把test.o和main.o打包成一個靜態庫文件lib_test.lib,輸入命令:

<code>ar rv lib_test.lib test.o main.o/<code>
靜態鏈接與動態鏈接


其實,用MinGW可以生成.a後綴和.lib後綴的靜態鏈接庫,這裡生成的是.lib後綴的靜態庫。此時文件夾下多出了.lib文件:

靜態鏈接與動態鏈接


然後把這個靜態庫鏈接成可執行文件lib_test.exe,輸入命令:

<code>gcc lib_test.lib -o lib_test.exe/<code>
靜態鏈接與動態鏈接


此時文件夾下多出了可執行文件lib_test.exe:

靜態鏈接與動態鏈接


雙擊運行:

靜態鏈接與動態鏈接

可見,運行結果與預期一致,說明我們使用靜態鏈接的方式生成的可執行文件沒問題。這個可執行文件的運行並不依賴於lib_test.lib文件,我們可以試著把這個文件刪除之後再運行,仍然可以正常運行。


2、動態鏈接實驗

我們把test.c編譯成動態庫文件dll_test.dll,輸入命令:

<code>gcc test.c -shared -o dll_test.dll /<code>
靜態鏈接與動態鏈接


此時文件夾下多出了動態庫文件dll_test.dll:

靜態鏈接與動態鏈接


我們用該動態庫文件dll_test.dll與main.c一起編譯生成可執行文件dll_test.exe,輸入命令:

<code>gcc dll_test.dll main.c -o dll_test.exe/<code>
靜態鏈接與動態鏈接


此時文件夾下多出了可執行文件dll_test.exe:

靜態鏈接與動態鏈接


雙擊運行:

靜態鏈接與動態鏈接


可見,運行結果與預期一致,說明我們使用動態鏈接的方式生成的可執行文件沒問題。這個可執行文件的運行依賴於dll_test.dll文件,我們可以試著把這個文件刪除之後再運行。運行出現如下錯誤:

靜態鏈接與動態鏈接


那是因為使用動態鏈接庫的應用程序不是自完備的,它依賴的DLL模塊也要存在。動態鏈接的方式使用得很廣泛,比如我們電腦系統盤的System32文件夾下就有很多動態鏈接庫文件:

靜態鏈接與動態鏈接


騰訊QQ安裝目錄下:

靜態鏈接與動態鏈接


上面使用的命令其實與Linux下操作的命令大多都很相似,我們只要明白這麼一回事就可以很快地在Linux下進行操作。


以上就是本次的筆記分享,如有錯誤,歡迎指出!謝謝。


分享到:


相關文章: