一、分別編譯與鏈接(Linking)
大多數高級語言都支持分別編譯,程序員可以顯式地把程序划分為獨立的模塊或文件,然后每個獨立部分分別編譯。在編譯之后,由鏈接器把這些獨立的片段(稱為編譯單元)“粘接到一起”。(想想這樣做有什么好處?)
在C/C++中,這些獨立的編譯單元包括obj文件(一般的源程序編譯而成)、lib文件(靜態鏈接的函數庫)、dll文件(動態鏈接的函數庫)等。
靜態鏈接方式:在程序執行之前完成所有的組裝工作,生成一個可執行的目標文件(EXE文件)。
動態鏈接方式:在程序已經為了執行被裝入內存之后完成鏈接工作,並且在內存中一般只保留該編譯單元的一份拷貝。
二、靜態鏈接庫與動態鏈接庫
先來闡述一下DLL(Dynamic Linkable Library)的概念,你可以簡單的把DLL看成一種倉庫,它提供給你一些可以直接拿來用的變量、函數或類。
靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果采用靜態鏈接庫,則無論你願不願意,lib中的指令都被直接包含在最終生成的EXE文件中了。但是若使用DLL,該DLL不必被包含在最終的EXE文件中,EXE文件執行時可以“動態”地引用和卸載這個與EXE獨立的DLL文件。
采用動態鏈接庫的優點:(1)更加節省內存;(2)DLL文件與EXE文件獨立,只要輸出接口不變,更換DLL文件不會對EXE文件造成任何影響,因而極大地提高了可維護性和可擴展性。
三、認識動態鏈接庫
動態鏈接是相對於靜態鏈接而言的。所謂靜態鏈接是指把要調用的函數或者過程鏈接到可執行文件中,成為可執行文件的一部分。換句話說,函數和過程的代碼就在程序的exe文件中,該文件包含了運行時所需的全部代碼。當多個程序都調用相同函數時,內存中就會存在這個函數的多個拷貝,這樣就浪費了寶貴的內存資源。而動態鏈接所調用的函數代碼並沒有被拷貝到應用程序的可執行文件中去,而是僅僅在其中加入了所調用函數的描述信息(往往是一些重定位信息)。僅當應用程序被裝入內存開始運行時,在Windows的管理下,才在應用程序與相應的DLL之間建立鏈接關系。當要執行所調用DLL中的函數時,根據鏈接產生的重定位信息,Windows才轉去執行DLL中相應的函數代碼。一般情況下,如果一個應用程序使用了動態鏈接庫,Win32系統保證內存中只有DLL的一份復制品
動態鏈接庫的兩種鏈接方法:
(1) 裝載時動態鏈接(Load-time Dynamic Linking):這種用法的前提是在編譯之前已經明確知道要調用DLL中的哪幾個函數,編譯時在目標文件中只保留必要的鏈接信息,而不含DLL函數的代碼;當程序執行時,調用函數的時候利用鏈接信息加載DLL函數代碼並在內存中將其鏈接入調用程序的執行空間中(全部函數加載進內存),其主要目的是便於代碼共享。(動態加載程序,處在加載階段,主要為了共享代碼,共享代碼內存)
(2) 運行時動態鏈接(Run-time Dynamic Linking):這種方式是指在編譯之前並不知道將會調用哪些DLL函數,完全是在運行過程中根據需要決定應調用哪個函數,將其加載到內存中(只加載調用的函數進內存),並標識內存地址,其他程序也可以使用該程序,並用LoadLibrary和GetProcAddress動態獲得DLL函數的入口地址。(dll在內存中只存在一份,處在運行階段)
上述的區別主要在於階段不同,編譯器是否知道進程要調用的dll函數。動態加載在編譯時知道所調用的函數,而在運行態時則必須不知道。