C語言學習篇(28)——函數庫

引言

我們在編寫代碼時,常常會寫一個.c源文件和與之對應的.h文件(例如a.c和a.h),並在源文件.c中定義(具體實現)一些函數,在.h文件中聲明, 這樣我們就可以在其他源文件中包含該頭文件,來調用之前寫的函數,這樣做的好處就是避免了重複勞動,同時也可以調用一些大神的代碼。 但是有時候我們不想公開自己的源代碼(.c),尤其是商業性代碼,同時又要讓客戶能夠使用他們編寫的代碼,解決的方法就是打包成靜態鏈接庫(.a,.lib)或動態鏈接庫(.so, .dll),同時提供對應的頭文件.h, 譬如我們經常使用的stdio.h , string.h等C庫函數,這些統稱為函數庫, 那麼我們今天就來聊聊什麼是函數庫,函數庫有什麼作用,如何製作函數庫等等話題。

什麼是函數庫

函數庫就是一些事先寫好的函數集合。 因為函數是模塊化的,不同的功能封裝成一個個不同的函數,可以是被反覆調用,當A寫好了一個功能函數,然後共享出來,此時B 有同樣的需求就不用自己重寫實現該功能了,直接調用A寫的函數即可,避免了重複勞動。 而且由於共享免費的原因,這些函數會經過很多人測試使用,並不斷優化,其可靠性是非常之高的,最終形成標準,譬如glibc。

函數庫的提供形式

目前函數庫主要的提供2種形式:動態鏈接庫和靜態鏈接庫。

比較早出現的是 靜態鏈接庫,其實質是商業公司或個人將自己的函數庫源代碼經過只編譯不鏈接,形成各個.o目標文件,然後用ar(gcc中 binutils 工具集,主要用來創建和管理連接程序使用的目標庫文檔)工具將各個.o(windows下.obj)文件歸檔成.a(或.lib),即靜態鏈接庫文件,然後通過發佈.a庫文件和.h頭文件來提供靜態庫給他人使用,而其他人則通過.h頭文件得知庫中的庫函數原型,然後在自己的.c文件中直接調用這些函數,並且在編譯鏈接時,鏈接器(ld)會去.a文件中拿出被調用的哪個函數編譯後的.o二進制代碼段鏈接形成最終的可執行程序。這就是靜態鏈接庫的原理。

動態鏈接庫比靜態鏈接庫出現的晚一些,那麼動態鏈接庫和靜態鏈接庫有什麼區別呢? 靜態庫是指鏈接器從靜態鏈接庫獲取所有被引用函數,並將這些函數加入到可執行文件中。這樣做的好處是代碼裝載速度快,但是缺點很明顯可執行文件體積較大,包含相同的公共代碼,造成浪費。而動態鏈接庫呢,避免了重複加載相同的代碼塊,因此更節省空間,但是其本身程序是不完整的,在整體運行速度比靜態庫慢。 我們通過一個簡單的hello world例子來感受下:

<code>#include 

int main(void)
{
	printf("hello world.\n");
	
	return 0;
}/<code>

我們調用glic庫中stdio中的printf函數,打印hello world, 然後我們編譯運行(這裡我們不加任何參數選項,即默認使用動態鏈接的方式),並通過ls -l命令查看編譯生成的可執行文件a.out的文件大小:8.4K

C語言學習篇(28)——函數庫

接著我們在通過參數選項(-static),指定使用靜態鏈接方式再次編譯,看看此時可執行文件a.out的文件大小:891K

C語言學習篇(28)——函數庫

兩者方式都成功運行了“hello world”,但使用靜態鏈接方式得到的可執行文件遠遠大於使用動態鏈接庫方式。

靜態鏈接方式中,在有多個應用程序都使用了這個庫函數時,結果會在多個應用程序最後生成的可執行程序中都各自有一份這個庫函數的代碼段。當這些應用程序同時在內存中運行時,實際上在內存中有多個這個庫函數的代碼段,這完全重複浪費了。而動態鏈接庫本身不將庫函數的代碼段鏈接入可執行程序,只是做個標記。然後當應用程序在內存中執行時,運行時環境發現它調用了一個動態庫中的庫函數時,會去加載這個動態庫到內存中,然後以後不管有多少個應用程序去調用這個庫中的函數都會跳轉到第一次加載的地方去執行(不會重複加載)。

C語言學習篇(28)——函數庫

總結

今天我們主要分析了函數庫的概念,以及2種表現方式:動態鏈接庫和靜態鏈接庫,並介紹了兩者的區別,以及如何使用靜態庫來編譯我們的代碼。之後我會講解如何不同平臺下(gcc, keil armcc下)製作我們自己靜態和動態鏈接庫,方便大家將自己的代碼共享給別人,同時也能保護自己的知識產權。好了,今天的分享到此,喜歡的話歡迎關注,轉發加收藏哦~


C語言學習篇(28)——函數庫



分享到:


相關文章: