? ? ? ? 學過C語言我們知道,C語言有標準庫和自定義庫,這些方便了我們的實際開發,提供了已經實現好的函數接口,我們使用的時候,只需要引入頭文件即可,那具體的實現過程又是怎么樣的呢?我們又該如何實現我們自己的庫呢?這就是我們本節需要講解的。
目錄
一、什么是庫文件
二、為什么需要庫
三、靜態庫的生成與使用
3.1 自定義靜態庫的生成
3.2 自定義靜態庫的使用
3.3 將自定義的靜態庫移動到系統庫目錄下使用
3.4 靜態庫刪除后是否可以使用
四、共享庫的生成與使用
4.1 自定義共享庫的生成
4.2 自定義共享庫的使用
4.2.1? 解決辦法1
4.2.2? 解決辦法2
4.3 共享庫刪除后是否可以使用
五、靜態庫和共享庫的區別
一、什么是庫文件
? ? ? ? 庫是一組預先編譯好的方法/函數的集合。 在 64 位的系統上有些庫也可能被存儲在/usr/lib64 下。?庫有兩種,一種是靜態庫,其命令規則為 libxxx.a,一種是共享庫,其命令規則為 libxxx.so。如下圖所示:
- Linux系統存儲庫的頭文件(函數的聲明)一般會被存儲在 /usr/include 下
- Linux系統存儲的庫(函數的具體實現)的位置一般在:/lib 和 /usr/lib
- Linux系統存儲的二進制可執行程序的位置在:/bin和/usr/bin
二、為什么需要庫
? ? ? ? ?因為在引用外部方法時,不需要改動直接用就行,但給的是.c文件的話,使用要編譯鏈接,很麻煩,所以直接生成.o文件,放到庫里,使用的時候直接鏈接就好。庫的隱蔽性較好,要讓對方使用方法,但又不想讓對象知道方法是如何讓實現的,就可以用庫。當同時有靜態庫和共享庫時,優先使用共享庫。
三、靜態庫的生成與使用
? ? ? 這里展示實現自定義的靜態庫,以及如何使用,需要個3文件:add.c? max.c? foo.h,其中“foo.h”中是函數的聲明,“add.c”和“max.c” 是函數的定義/具體實現
創建一個頭文件,包含所有自定義函數的聲明,后面再使用直接引|入自己創建的頭文件即可!
3.1 自定義靜態庫的生成
第一步:先將需要生成庫文件的所有“.c“文件編譯成“.o”文件
第二步:使用 ar 命令將第一步編譯的所有”.o”文件生成靜態庫,其中:c 是創建庫 ?r 是將方法添加到庫中? ??v 顯示過程。
至此,靜態庫已經生成啦,是不是非常簡單!注意:此時生成的靜態庫在當前目錄下,并不是系統庫文件位置,我們在下面使用的時候,要用雙引號引入頭文件即:#include '' foo. h'' 。
3.2 自定義靜態庫的使用
? ? ? ?上一小節,我們講解了如何生成靜態庫,那么,我們又該如何使用呢?
靜態庫的使用也很簡單,只需要在主函數文件里面引入我們自定義的靜態庫頭文件,然后編譯鏈接的時候,指定鏈接的靜態庫即可!
第一步:需要使用靜態庫的地方引入靜態庫對應的頭文件?
第二步:編譯鏈接的時候指定鏈接庫的位置
3.3 將自定義的靜態庫移動到系統庫目錄下使用
? ? ? ? ?當我們測試完自己的庫沒有問題后,這時候便可以將自定義的靜態庫移動到Linux系統存儲的庫的位置:/lib 和 /usr/lib,移動完之后,它就成了標準庫的一部分,后續我們的使用非常簡單,在頭文件的引入使用尖括號也可以了<>,此外,編譯鏈接的時候,也不需要指定靜態庫的位置了,因為它首先回會到標準庫位置下找,此時就可以找到。
3.4 靜態庫刪除后是否可以使用
四、共享庫的生成與使用
? ? 這里展示實現自定義的共享庫,以及如何使用,需要個3文件:add.c? max.c? foo.h,其中“foo.h”中是函數的聲明,“add.c”和“max.c” 是函數的定義/具體實現
創建一個頭文件,包含所有自定義函數的聲明,后面再使用直接引|入自己創建的頭文件即可!?
?
4.1 自定義共享庫的生成
第一步:先將需要生成庫文件的所有“.c“文件編譯成“.o”文件
第二步:使用 gcc 命令將第一步編譯的所有”.o”文件生成共享庫?
? ? ? ?在共享庫的創建過程中, -fPIC是一個編譯選項,用于生成位置無關的代碼。它告訴編譯器生成與位置無關的代碼,這對于共享庫特別重要。共享庫(也稱為動態鏈接庫)是一種在多個程序之間共享的庫。當程序加載時,操作系統會將共享庫映射到進程的地址空間中,以便程序可以調用庫中的函數和訪問庫中的變量。位置無關的代碼是一種在不同內存位置上執行時都能正常工作的代碼。這對于共享庫尤其重要,因為它們可能會在不同的內存地址空間中加載。-fPIC 選項使得編譯器生成的代碼能夠在內存中的任何位置執行,而不受具體加載地址的限制。它通常用于創建共享庫,以確保庫能夠在不同的進程中重定位到不同的內存地址,而不會發生沖突或錯誤。
至此,共享庫已經生成啦,是不是非常簡單!注意:此時生成的共享庫在當前目錄下,并不是系統庫文件位置,我們在下面使用的時候,要用雙引號引入頭文件即:#include '' foo. h'' 。
4.2 自定義共享庫的使用
? ? ? ?上一小節,我們講解了如何生成共享庫,那么,我們又該如何使用呢?
? ? ?共享庫該如何使用呢?我們先學習下靜態庫的使用,然后觀察是否可以使用,在主函數文件里面引入我們自定義的共享庫頭文件,然后編譯鏈接的時候,指定鏈接的共享庫的位置和庫名。
第一步:需要使用靜態庫的地方引入靜態庫對應的頭文件?
第二步:編譯鏈接的時候指定鏈接庫的位置??
? ? ? ? 以下是使用共享庫“libfoo.so”和“main.c”生成可執行文件的過程,其中 -L 指定庫的 存儲路徑, -l 指定庫的名稱(不需要前面的‘lib’和擴展名‘.so’), 如果在庫的存儲路徑有同名的共享庫和靜態庫,gcc 默認使用共享庫。
與靜態庫的使用對比,為什么這里會出現問題呢?又該如何解決呢?
這是因為:靜態庫和共享庫在加載庫時的行為是不同的
? ? ? ?1.靜態庫:靜態庫在鏈接時會被整體地復制到可執行文件中,因此可執行文件本身包含了靜態庫的代碼和數據。當程序運行時,靜態庫的代碼和數據已經被包含在可執行文件中,因此程序會直接調用其中的函數或訪問其中的變量,而不需要額外加載庫文件。因此,靜態庫在運行時不會去查找庫文件,而是直接使用可執行文件中已經包含的庫代碼和數據。? ? ? ? ?2.共享庫:共享庫在程序運行時才會被加載到內存中。程序在啟動時會查找共享庫,并將其加載到內存中的地址空間。通常,操作系統會預定義一些標準位置用于查找共享庫,比去存儲庫的標準位置(/lib 或/usr/lib 等)加載。程序在運行時會根據操作系統的規則去這些標準位置下查找共享庫。如果共享庫不存在于這些位置,那么程序可能會因為找不到庫而報錯。
? ? ? ?總的來說,靜態庫在鏈接時就已經被包含在可執行文件中,程序運行時不需要額外查找庫文件;而共享庫在運行時才會被加載到內存中,程序會按照操作系統的規則去標準位置下查找共享庫。
4.2.1? 解決辦法1
既然,共享庫會去標準位置下找對應的庫,那我們就可以將共享庫移動到標準庫位置下,這樣就不會報錯了。
4.2.2? 解決辦法2
如果庫不在標準位置下,我們放在當前位置下,也可以通過設置環境變量”LD_LIBRARY_PATH”來指定加載庫的路徑,這樣也就不會報錯了。
ldd命令可以查看當前可執行程序依賴哪些庫?
4.3 共享庫刪除后是否可以使用
? ? ? ?共享庫只有在運行的時候,才會去標準庫位置下去找對應的庫,不會進行拷貝,因此刪除后,可執行程序無法運行,共享庫只有一份,靜態庫會被拷貝到可執行程序里面,因此刪除后還可以使用。
五、靜態庫和共享庫的區別
? ? ? ? 靜態庫在鏈接時將用到的方法包含到最終生成的可執行程序中,成為可執行程序的一部分,而共享庫不包含,也就是說可執行程序里面是沒有實現的方法的,只做標記,在運行程序時,才動態加載。
靜態庫:
? ? ? ?用靜態庫編譯出來的程序,相當于是把靜態庫里的.o文件拷貝了一份到可執行程序里,因此即使將庫里的文件刪除,可執行程序依然能執行。如果庫里的數據需要更新,但原來庫里的.o文件已經成為了可執行程序的一部分,此時要用更新的方法,就要把可執行程序刪除,重新編譯鏈接一個新文件。因此,靜態庫刪除后,可執行程序仍然可以正常使用
共享庫:
? ? ? ?用共享庫編譯出來的程序,相當于是標記了所需方法,但可執行程序里沒有所需方法的實現,因此當所需方法在庫里刪除后,可執行程序找不到所需方法,文件就運行不了。
共享庫里的數據更新,可執行程序,在運行時直接尋找共享庫,就直接能用更新后的.o文件。因此,共享庫刪除后,可執行程序無法正常使用!
以上就是全部內容!請務必掌握,這是后續學習的基礎,歡迎大家點贊加關注評論,您的支持是我前進最大的動力!下期再見!