筆者一直以來(lái)都對(duì)mingw64下動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)鏈接的真正區(qū)別和用法存疑,于是做了一些測(cè)試,這篇文章記錄了測(cè)試過(guò)程和測(cè)試結(jié)果,如果只想知道結(jié)果可以跳轉(zhuǎn)到文章末尾
一、準(zhǔn)備工作
首先準(zhǔn)備三個(gè)測(cè)試文件
其中add.c最終會(huì)被編譯會(huì)靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù),被測(cè)試文件main.c調(diào)用
使用如下命令構(gòu)建靜態(tài)庫(kù)
gcc -c add.c -o add.o
ar rcs libadd.a add.o
得到靜態(tài)庫(kù)如下:
接著使用如下命令構(gòu)建動(dòng)態(tài)庫(kù)
gcc -fPIC -shared add.c -o libadd.dll
得到動(dòng)態(tài)庫(kù)如下:
二、測(cè)試過(guò)程
1.當(dāng)一個(gè)靜態(tài)庫(kù)和一個(gè)動(dòng)態(tài)庫(kù)重名,并且位于同一文件夾下,編譯器優(yōu)先鏈接哪一個(gè)
首先把五個(gè)文件放在同一路徑下
已知在編譯指令中,鏈接libadd.a 和鏈接libadd.dll都只需要加上以下參數(shù)
-ladd
也即這里靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)可以看做是重名的,于是我們先來(lái)測(cè)試當(dāng)輸入-ladd的時(shí)候,gcc會(huì)默認(rèn)鏈接哪個(gè)庫(kù)
輸入指令:
gcc main.c -I ./ -L ./ -ladd -o normal_two_implicit
得到文件
文件名是為了對(duì)比實(shí)驗(yàn)方便而設(shè)置的,其中normal意味著正常狀態(tài),與后面要進(jìn)行的反常測(cè)試相對(duì)應(yīng),two表示靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)同時(shí)位于同一文件夾下,與之后將某個(gè)庫(kù)移走單獨(dú)測(cè)試剩下的庫(kù)相對(duì)應(yīng),implicit意為在鏈接命令中隱式指出庫(kù)名稱(chēng)(即-l格式的指明方法),與之后顯示給出庫(kù)的全稱(chēng)的測(cè)試對(duì)應(yīng)。
雖然現(xiàn)在得到了可執(zhí)行文件,但是現(xiàn)在我們還無(wú)法判斷默認(rèn)鏈接的是哪一個(gè)庫(kù),于是繼續(xù)進(jìn)行測(cè)試
先把libadd.dll暫時(shí)移到其他文件夾里,只保留libadd.a,再鍵入同樣的命令:
gcc main.c -I ./ -L ./ -ladd -o normal_a_implicit
得到文件
這里的exe一定鏈接的是靜態(tài)庫(kù),而且和上一個(gè)exe大小完全相同,因此合理推測(cè)前一個(gè)exe也是鏈接了靜態(tài)庫(kù),也即:當(dāng)兩種同名庫(kù)同時(shí)存在于同一文件夾,并且使用-l格式參數(shù)隱式指出鏈接庫(kù)名稱(chēng)時(shí),gcc優(yōu)先鏈接后綴為.a的庫(kù)(為什么不說(shuō)優(yōu)先鏈接靜態(tài)庫(kù)?后續(xù)的測(cè)試會(huì)說(shuō)明)
然后把libadd.a暫時(shí)移到其他文件夾里,只保留libadd.dll,再鍵入同樣的命令:
gcc main.c -I ./ -L ./ -ladd -o normal_dll_implicit
得到文件
文件大小只有54KB,小于前兩個(gè),因此這是個(gè)鏈接了動(dòng)態(tài)庫(kù)的exe
因此得到結(jié)論:當(dāng)一個(gè)靜態(tài)庫(kù)和一個(gè)動(dòng)態(tài)庫(kù)重名,并且位于同一文件夾下,編譯器優(yōu)先鏈接后綴.a的庫(kù)
2.-static參數(shù)有什么用
之前一直以為,加了-static就是靜態(tài)鏈接,做出來(lái)的exe一定就是那種完全可以移植的,但是經(jīng)過(guò)測(cè)試以后我發(fā)現(xiàn)我大錯(cuò)特錯(cuò)
首先把libadd.a和libadd.dll放回原位
然后輸入以下命令:
gcc -static main.c -I ./ -L ./ -ladd -o normal_two_implicit_static
得到:
文件大小為64KB,這說(shuō)明不出所料地,編譯器優(yōu)先鏈接了靜態(tài)庫(kù),這和-static關(guān)鍵字很搭,接下來(lái)把libadd.dll暫時(shí)移走,只留下libadd.a,輸入以下命令
gcc -static main.c -I ./ -L ./ -ladd -o normal_a_implicit_static
得到
64KB,鏈接了靜態(tài)庫(kù),依然在意料之中
這次移走libadd.a,只留下libadd.dll,然后輸入以下命令
gcc -static main.c -I ./ -L ./ -ladd -o normal_dll_implicit_static
錯(cuò)誤出現(xiàn)了:
錯(cuò)誤信息為并沒(méi)有找到庫(kù)文件!這和我們的預(yù)設(shè)也一致,因?yàn)?static禁用了動(dòng)態(tài)庫(kù),所以不再能找得到動(dòng)態(tài)庫(kù)dll
測(cè)試至此本可以結(jié)束,但是這里仍然存在一個(gè)疑問(wèn):我們到目前為止還沒(méi)有通過(guò)顯式的指出文件名來(lái)鏈接庫(kù),因?yàn)槠浣Y(jié)果似乎不用想都可以知道,那就是給出什么文件名就鏈接哪個(gè)文件,不可能出現(xiàn)重名的情況,這當(dāng)然是對(duì)的,但是在-static關(guān)鍵字的加持下,似乎在另一個(gè)角度出現(xiàn)了有意思的現(xiàn)象
再一次把文件歸位在一起
鍵入如下命令:
gcc -static main.c -I ./ -L ./ libadd.a -o normal_a_explicit_static
得到文件
沒(méi)有問(wèn)題
然后鍵入如下命令
gcc -static main.c -I ./ -L ./ libadd.dll -o normal_dll_explicit_static
根據(jù)之前的經(jīng)驗(yàn),我猜測(cè):由于-static禁用了動(dòng)態(tài)庫(kù)鏈接的選項(xiàng),那么這條命令應(yīng)該也會(huì)失敗并報(bào)錯(cuò),但是奇怪的事情發(fā)生了,命令正常運(yùn)行并且得到了正常的動(dòng)態(tài)庫(kù)鏈接exe(因?yàn)樗拇笮?4KB)
至此我們得到了第二個(gè)十分有用的結(jié)論:-static并不禁用動(dòng)態(tài)鏈接,它只是禁用了采用隱式指明庫(kù)名法(即以-l格式指明庫(kù)名)時(shí)動(dòng)態(tài)鏈接的選項(xiàng),如果你愿意的話完全可以在有-static的情況下輸入完整的動(dòng)態(tài)庫(kù)dll的全稱(chēng)來(lái)進(jìn)行動(dòng)態(tài)鏈接
3.編譯器靠什么識(shí)別一個(gè)庫(kù)時(shí)靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù)(后綴還是二進(jìn)制內(nèi)容)
這個(gè)疑問(wèn)乍看起來(lái)有些多次一舉:后綴為.a的就是靜態(tài)庫(kù),后綴為.dll的就是動(dòng)態(tài)庫(kù)呀。
但是事實(shí)真的如此嗎?我們完全可以通過(guò)修改后綴名而保持它們的二進(jìn)制數(shù)據(jù)并不變化,修改以后真正的靜態(tài)庫(kù)后綴為.dll,而真正的動(dòng)態(tài)庫(kù)后綴為.a,。在這種情況下如果將.a鏈接進(jìn)去,編譯器會(huì)辨別出它實(shí)際上是個(gè)披著靜態(tài)庫(kù)外皮的動(dòng)態(tài)庫(kù)呢,還是會(huì)傻傻地將其當(dāng)做靜態(tài)庫(kù)打包進(jìn)exe?于是有了下面的測(cè)試:
修改后綴名的過(guò)程略去
經(jīng)過(guò)修改后綴名,現(xiàn)在這里的.a才是真正的動(dòng)態(tài)庫(kù),而.dll才是真正的靜態(tài)庫(kù)
鍵入以下命令:
gcc main.c -I ./ -L ./ -ladd -o unnormal_two_implicit
得到文件
文件只有54KB,這說(shuō)明它實(shí)際上鏈接了動(dòng)態(tài)庫(kù),也即現(xiàn)在的.a庫(kù),這與我們測(cè)試1中的結(jié)論也相同,即:當(dāng)同名庫(kù)同時(shí)存在時(shí),隱式指明庫(kù)名會(huì)默認(rèn)優(yōu)先鏈接.a庫(kù)(這也是為什么前面不說(shuō)優(yōu)先鏈接靜態(tài)庫(kù)的原因,因?yàn)?a不一定是靜態(tài)庫(kù),在本例中它是一個(gè)披著靜態(tài)庫(kù)后綴的動(dòng)態(tài)庫(kù))
于是得到有用的第三條結(jié)論:編譯器靠二進(jìn)制內(nèi)容識(shí)別靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),而不會(huì)被其后綴名所迷惑
三、結(jié)論
1.當(dāng)一個(gè)靜態(tài)庫(kù)和一個(gè)動(dòng)態(tài)庫(kù)重名,并且位于同一文件夾下,編譯器優(yōu)先鏈接后綴.a的庫(kù)
2.-static并不禁用動(dòng)態(tài)鏈接,它只是禁用了采用隱式指明庫(kù)名法(即以-l格式指明庫(kù)名)時(shí)動(dòng)態(tài)鏈接的選項(xiàng),如果你愿意的話完全可以在有-static的情況下輸入完整的動(dòng)態(tài)庫(kù)dll的全稱(chēng)來(lái)進(jìn)行動(dòng)態(tài)鏈接
3.編譯器靠二進(jìn)制內(nèi)容識(shí)別靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),而不會(huì)被其后綴名所迷惑
下面以編程人員的角度,根據(jù)以上三條結(jié)論再導(dǎo)出出幾條實(shí)用結(jié)論:
Q:當(dāng)需要把一個(gè)dll鏈接進(jìn)程序的時(shí)候該怎么做?
A:在不確定是否有重名庫(kù)的情況下,建議直接在編譯參數(shù)中顯式給出庫(kù)名的全稱(chēng),如果確定沒(méi)有重名靜態(tài)庫(kù)的話,可以考慮使用-l的參數(shù)指出dll的名稱(chēng)
Q:當(dāng)需要進(jìn)行靜態(tài)編譯,即把所有庫(kù)打包放進(jìn)exe以保證其良好的不受環(huán)境依賴(lài)性的時(shí)候該怎么做?
A:只加一條-static并不會(huì)完事大吉,建議仔細(xì)檢查給出的庫(kù)中不包含動(dòng)態(tài)庫(kù),然后加上-static參數(shù)作為最終檢查
Q:我要開(kāi)發(fā)一個(gè)程序,程序運(yùn)行時(shí)需要調(diào)用一些第三方動(dòng)態(tài)庫(kù)如opencvworld455.dll,但是我又希望我編譯出的exe可以單獨(dú)發(fā)布出去讓其他人運(yùn)行,可以做到把opencvworld455.dll靜態(tài)鏈接到exe中嗎?
A:不能這么做,動(dòng)態(tài)庫(kù)無(wú)法靜態(tài)鏈接,這是庫(kù)的二進(jìn)制內(nèi)容差異決定的,要發(fā)布依賴(lài)第三方dll的可執(zhí)行文件exe,必須同時(shí)打包發(fā)布其依賴(lài)的第三方dll(有些商業(yè)軟件為了保證在一些連基本dll環(huán)境如msvcr.dll都沒(méi)有的機(jī)器上運(yùn)行,甚至?xí)⑷縟ll和exe一起打包發(fā)布)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-448582.html
四、注
1.本文省略了一些與主題相關(guān)性不高的測(cè)試過(guò)程,比如測(cè)試第一個(gè)問(wèn)題,重名問(wèn)題時(shí),并沒(méi)有給出顯式指明庫(kù)名的結(jié)果,以及第三個(gè)問(wèn)題,編譯器靠什么識(shí)別一個(gè)庫(kù)時(shí)靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù)問(wèn)題中,也沒(méi)有給出加上-static之后的測(cè)試結(jié)果,另外僅憑文件大小就判斷鏈接了靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)未免有些草率,這些問(wèn)題都從某種程度上顯得測(cè)試的邏輯性不夠嚴(yán)密。但實(shí)際上筆者幾乎做了一切可以想到的測(cè)試,并且通過(guò)移走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é)果顯而易見(jiàn)和篇幅限制(懶)的緣故在文章中沒(méi)有給出,感興趣的讀者可以自己試試
2.本文測(cè)試的平臺(tái)為mingw64+windows10,結(jié)論并不一定適用于其他平臺(tái)
3.如有錯(cuò)誤,歡迎指正文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-448582.html
到了這里,關(guān)于mingw64下動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)鏈接的真正區(qū)別和用法(詳細(xì))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!