筆者一直以來都對(duì)mingw64下動(dòng)態(tài)庫和靜態(tài)庫鏈接的真正區(qū)別和用法存疑,于是做了一些測(cè)試,這篇文章記錄了測(cè)試過程和測(cè)試結(jié)果,如果只想知道結(jié)果可以跳轉(zhuǎn)到文章末尾
首先準(zhǔn)備三個(gè)測(cè)試文件
gcc -c add.c -o add.o
ar rcs libadd.a add.o
得到靜態(tài)庫如下:
gcc -fPIC -shared add.c -o libadd.dll
得到動(dòng)態(tài)庫如下:
首先把五個(gè)文件放在同一路徑下
-ladd
也即這里靜態(tài)庫和動(dòng)態(tài)庫可以看做是重名的,于是我們先來測(cè)試當(dāng)輸入-ladd的時(shí)候,gcc會(huì)默認(rèn)鏈接哪個(gè)庫
輸入指令:
gcc main.c -I ./ -L ./ -ladd -o normal_two_implicit
得到文件
gcc main.c -I ./ -L ./ -ladd -o normal_a_implicit
得到文件
gcc main.c -I ./ -L ./ -ladd -o normal_dll_implicit
得到文件
因此得到結(jié)論:當(dāng)一個(gè)靜態(tài)庫和一個(gè)動(dòng)態(tài)庫重名,并且位于同一文件夾下,編譯器優(yōu)先鏈接后綴.a的庫
之前一直以為,加了-static就是靜態(tài)鏈接,做出來的exe一定就是那種完全可以移植的,但是經(jīng)過測(cè)試以后我發(fā)現(xiàn)我大錯(cuò)特錯(cuò)
首先把libadd.a和libadd.dll放回原位
gcc -static main.c -I ./ -L ./ -ladd -o normal_two_implicit_static
得到:
gcc -static main.c -I ./ -L ./ -ladd -o normal_a_implicit_static
得到
gcc -static main.c -I ./ -L ./ -ladd -o normal_dll_implicit_static
錯(cuò)誤出現(xiàn)了:
gcc -static main.c -I ./ -L ./ libadd.a -o normal_a_explicit_static
得到文件
gcc -static main.c -I ./ -L ./ libadd.dll -o normal_dll_explicit_static
根據(jù)之前的經(jīng)驗(yàn),我猜測(cè):由于-static禁用了動(dòng)態(tài)庫鏈接的選項(xiàng),那么這條命令應(yīng)該也會(huì)失敗并報(bào)錯(cuò),但是奇怪的事情發(fā)生了,命令正常運(yùn)行并且得到了正常的動(dòng)態(tài)庫鏈接exe(因?yàn)樗拇笮?4KB)
這個(gè)疑問乍看起來有些多次一舉:后綴為.a的就是靜態(tài)庫,后綴為.dll的就是動(dòng)態(tài)庫呀。
但是事實(shí)真的如此嗎?我們完全可以通過修改后綴名而保持它們的二進(jìn)制數(shù)據(jù)并不變化,修改以后真正的靜態(tài)庫后綴為.dll,而真正的動(dòng)態(tài)庫后綴為.a,。在這種情況下如果將.a鏈接進(jìn)去,編譯器會(huì)辨別出它實(shí)際上是個(gè)披著靜態(tài)庫外皮的動(dòng)態(tài)庫呢,還是會(huì)傻傻地將其當(dāng)做靜態(tài)庫打包進(jìn)exe?于是有了下面的測(cè)試:
修改后綴名的過程略去
gcc main.c -I ./ -L ./ -ladd -o unnormal_two_implicit
得到文件
1.當(dāng)一個(gè)靜態(tài)庫和一個(gè)動(dòng)態(tài)庫重名,并且位于同一文件夾下,編譯器優(yōu)先鏈接后綴.a的庫
2.-static并不禁用動(dòng)態(tài)鏈接,它只是禁用了采用隱式指明庫名法(即以-l格式指明庫名)時(shí)動(dòng)態(tài)鏈接的選項(xiàng),如果你愿意的話完全可以在有-static的情況下輸入完整的動(dòng)態(tài)庫dll的全稱來進(jìn)行動(dòng)態(tài)鏈接
3.編譯器靠二進(jìn)制內(nèi)容識(shí)別靜態(tài)庫和動(dòng)態(tài)庫,而不會(huì)被其后綴名所迷惑
下面以編程人員的角度,根據(jù)以上三條結(jié)論再導(dǎo)出出幾條實(shí)用結(jié)論:
Q:當(dāng)需要把一個(gè)dll鏈接進(jìn)程序的時(shí)候該怎么做?
A:在不確定是否有重名庫的情況下,建議直接在編譯參數(shù)中顯式給出庫名的全稱,如果確定沒有重名靜態(tài)庫的話,可以考慮使用-l的參數(shù)指出dll的名稱
Q:當(dāng)需要進(jìn)行靜態(tài)編譯,即把所有庫打包放進(jìn)exe以保證其良好的不受環(huán)境依賴性的時(shí)候該怎么做?
A:只加一條-static并不會(huì)完事大吉,建議仔細(xì)檢查給出的庫中不包含動(dòng)態(tài)庫,然后加上-static參數(shù)作為最終檢查
Q:我要開發(fā)一個(gè)程序,程序運(yùn)行時(shí)需要調(diào)用一些第三方動(dòng)態(tài)庫如opencvworld455.dll,但是我又希望我編譯出的exe可以單獨(dú)發(fā)布出去讓其他人運(yùn)行,可以做到把opencvworld455.dll靜態(tài)鏈接到exe中嗎?
A:不能這么做,動(dòng)態(tài)庫無法靜態(tài)鏈接,這是庫的二進(jìn)制內(nèi)容差異決定的,要發(fā)布依賴第三方dll的可執(zhí)行文件exe,必須同時(shí)打包發(fā)布其依賴的第三方dll(有些商業(yè)軟件為了保證在一些連基本dll環(huán)境如msvcr.dll都沒有的機(jī)器上運(yùn)行,甚至?xí)⑷縟ll和exe一起打包發(fā)布)
1.本文省略了一些與主題相關(guān)性不高的測(cè)試過程,比如測(cè)試第一個(gè)問題,重名問題時(shí),并沒有給出顯式指明庫名的結(jié)果,以及第三個(gè)問題,編譯器靠什么識(shí)別一個(gè)庫時(shí)靜態(tài)庫還是動(dòng)態(tài)庫問題中,也沒有給出加上-static之后的測(cè)試結(jié)果,另外僅憑文件大小就判斷鏈接了靜態(tài)庫和動(dòng)態(tài)庫未免有些草率,這些問題都從某種程度上顯得測(cè)試的邏輯性不夠嚴(yán)密。但實(shí)際上筆者幾乎做了一切可以想到的測(cè)試,并且通過移走dll再運(yùn)行exe發(fā)現(xiàn)不能運(yùn)行才判斷其為動(dòng)態(tài)鏈接產(chǎn)物,發(fā)現(xiàn)能運(yùn)行才判斷其為靜態(tài)鏈接產(chǎn)物,這些測(cè)試結(jié)果由于結(jié)果顯而易見和篇幅限制(懶)的緣故在文章中沒有給出,感興趣的讀者可以自己試試
2.本文測(cè)試的平臺(tái)為mingw64+windows10,結(jié)論并不一定適用于其他平臺(tái)
3.如有錯(cuò)誤,歡迎指正
聯(lián)系客服