由於windows
和linux
的平台不同(主要是編譯器、彙編器和聯結器的不同),因此二者庫的二進位制是不相容的),本文僅僅介紹linux
系統下的庫。
庫就是寫好的現有的,成熟的,可以復用的**。現實中每個程式都要依賴很多基礎的底層庫,不可能每個人的**都從零開始,因此庫的存在意義非同尋常。
本質上來說庫是一種可執行**的二進位制形式,可以被作業系統載入記憶體執行。
庫有兩種:靜態庫(.a
、.lib
)和動態庫(.so
、.dll
)。windows
上對應的是.lib
和.dll
,linux
上對應的是.a
和.so
。
本文主要講解linux
下的靜態庫和動態庫的建立和使用。
gcc -c ***.c -o ***.o
:這個命令就是將我們編寫的原始檔***.c
(.c檔案或者是.cpp
檔案) 編譯為目標檔案***.o
(.o檔案)。
gcc ***1.o ***2.o -o ***.out
:將上面生成的多個.o
檔案鏈結生成乙個可執行檔案***.out
。不加-o
選項預設生成的可執行檔名為a.out
。
首先準備好測試**:我們這裡使用vscode
編寫三個檔案 :hello.h
、hello.c
和main.c
檔案。
檔案的結構也是我們最熟悉的,檔案結構如下:
|-- hello.h
|-- hello.c
|-- main.c
在hello.h
這個標頭檔案中我們只宣告函式printhel
而不定義,而是在hello.c
原始檔中進行定義, 而在main.c
中呼叫該函式。
三個程式檔案**如下:
hello.h
標頭檔案包含的是函式的宣告:
hello.h
#ifndef hello_h_
#define hello_h_
void
printhel()
;#endif
hello.c
包含的是標頭檔案的中宣告函式的定義:
hello.c
#include
"hello.h"
#include
void
printhel()
main.c
檔案中呼叫了該函式:
至此,我們的測試**就準備好了,雖然很簡單,但是還是能說明問題的,下面繼續。/main.c./
#include
#include
"hello.h"
intmain()
處理思路:
我們有幾種方法編譯這幾個檔案生成最終的可執行檔案:
1) 通過編譯多個原始檔,直接將目標**合成乙個.o檔案。
2) 通過建立靜態鏈結庫libhel.a
,使得main函式呼叫printhel
函式時可呼叫靜態鏈結庫。
3)通過建立動態鏈結庫libhel.so
,使得main函式呼叫printhel
函式時可呼叫靜態鏈結庫。
編譯hello.c
:
# gcc -c hello.c -o hello.o
會生成hello.o
目標檔案。
同理編譯main.c
:
# gcc -c main.c -o main.o
會生成main.o
目標檔案。
# gcc hello.o main.o -o execuate
會生成execuate
這個檔案。
執行可執行檔案:
# ./execuate
hello!
至此,我們的編譯工作就算是完成了,但是如果工程很大的話,有多個標頭檔案和原始檔的話,這樣編譯很麻煩,而且產生的可執行檔案很大,會造成空間浪費。最好的方法就是將hello.c
編譯為庫,其中的函式就成為了庫函式。這和math
庫沒有什麼區別。
下面我們會僅此呢個實操,將原始檔編譯成靜態庫和動態庫。
有了上面的基礎之後,我們分別介紹一下什麼是靜態庫,什麼是動態庫,下面先介紹靜態庫。
之所以稱為 「靜態庫」,是因為在鏈結階段,會將彙編生成的目標檔案.o
與引用到的庫一起鏈結打包到可執行檔案中。因此對應的鏈結方式稱為靜態鏈結。
試想一下,靜態庫與彙編生成的目標檔案一起鏈結為可執行檔案,那麼靜態庫必定跟.o
檔案格式相似。其實乙個靜態庫可以簡單看成是一組目標檔案(.o
檔案)的集合,即很多目標檔案經過壓縮打包後形成的乙個檔案。
靜態庫特點總結:
靜態庫對函式庫的鏈結是放在編譯時期完成的。
程式在執行時與函式庫再無瓜葛,移植方便。
在linux
下靜態庫的名字一般是lib***.a
。其中***
是該庫的名字,字尾是.a
。
# gcc -c hello.c -o hello.o
然後檢視一下果然多了乙個hello.o
檔案:
# ls
hello.c hello.h hello.o main.c
ar -cr libhel.a hello.o
引數解析:
-c
:表示建立
-r
: 表示replace
,如果存在則用新生成的進行替換。
再檢視目錄的話就會多出來乙個名為libhel.a
的檔案,這個就是生成的靜態庫。
在使用靜態庫之前還是來熟悉一下幾個# ls
hello.c hello.h hello.o libhel.a main.c
gcc
的選項引數:
-l
:-l
選項指定額外的函式庫的搜尋路徑,如果不指定的話,則只會搜尋gcc
的預設路徑,比如有:/usr/lib
等,不會搜尋到我們剛才建立的靜態庫。
-l
:-l
選項是指在連線時候指定連線的函式庫名字。比如這裡就是函式庫名 :-lhel
。
注意:-l 後面不需要完成的名字,而是去掉了前面的lib
字首以及.a
字尾名。
# gcc main.c -l. -lhel -o execuate
至此那個可執行檔案:
動態庫的建立和使用和上面的靜態庫使用差不多,只不過個別的命令的變化../execuate
hello!
由目標檔案.o
生成動態庫:
# gcc -shared -fpic -o libmyhello.so hello.o
然後就會生成libmyhello.so
動態庫。在這李我們可以看出乙個區別就是建立動態庫不需要ar
等打包工具,直接由gcc
編譯器就可建立。
2. 使用動態庫
使用的命令和使用靜態庫是一樣的:
# gcc main.c -l. -lmyhello -o hhh
上述命令生成可執行檔案hhh
。
執行生成的可執行檔案
./hhh
error while loading shared libraries: libmyhello.so: cannot open shared object file: no such file or directory
錯誤提示,找不到動態庫檔案libmyhello.so。程式在執行時,會查詢需要的動態庫檔案。若找到,則載入動態庫,否則將提示類似上述錯誤而終止程式執行。有多種方法可以解決。
最簡單的就是將檔案libmyhello.so
複製到目錄/usr/lib
中,因為/usr/lib
是gcc
編譯器查詢庫的預設路徑之一:
然後執行:$ sudo
cp libmyhello.so /usr/lib
參考資料:./hhh
hello!
每天進步一點點!
2020/8/09 成都
linux 下靜態庫和動態庫
我們通常把一些公用函式製作成函式庫,供其它程式使用。函式庫分為靜態庫和動態庫兩種。靜態庫在程式編譯時會被連線到目標 中,程式執行時將不再需要該 靜態庫。動態庫在程式編譯時並不會被連線到目標 中,而是在程式執行是才被載入,因此在程式執行時還需要動態庫存在。本文主要通過舉例來說明在 linux中如何建立...
linux下的靜態庫和動態庫
庫是什麼?在我們實際程式設計中,經常會引入各種庫函式,那麼庫是什麼?從本質上來說是一種可執行 的二進位制格式,可以被載入記憶體中執行。庫分靜態庫和動態庫兩種。靜態庫 linux下靜態庫的名字一般是lib a,為庫的名字。利用靜態函式庫編譯成的檔案比較大,因為整個函式庫的所有資料都會被整合進目標 中,...
linux下的靜態庫和動態庫
意義 為了避免 的重寫率,我們可以將已經寫好的 形成乙個庫,當我們再次用到的時候可以直接呼叫,而不是重新去寫,即 站在巨人的肩膀上 linux下有兩種庫 一 靜態庫 準備工作 int swap int x,int y include int main 1 將我們需要的函式生成乙個.o 檔案 root...