鏈接靜態庫必須用靜態編譯嗎
1. 動態鏈接,靜態鏈接 動態編譯,靜態編譯 動態鏈接庫,靜態鏈接庫 這些兩兩間的區別是什麼呢
有點兒亂……,分數少簡單扼要的說說吧。
————————————
首先,傳統的編譯,也就是靜態編譯是把 源文件 翻譯成目標文件,這個是一次性過程,也就是你所謂的靜態編譯。
後來的Java和.NET等語言,首先編譯成中間形式,然後運行過程中根據需要編譯成本地代碼(注意這個過程不是一次性的,下次運行重新編譯),這個就是JIT(即時編譯)技術,從即時編譯發展出了動態編譯技術
————————————
(傳統的)編譯完成後,像C/C++、Fortran、匯編等語言,可以把多個目標文件合並到一個庫文件中,這個就是靜態庫。比如常說的庫函數printf就是libc裡面的函數。
如果有了啟動函數(main),main裡面使用了printf,就可以通過靜態鏈接技術,從libc中提取出printf所在的文件加入到可執行文件中,如果printf還需要其它函數,就繼續搜索並加入列表,直到形成一個閉包。這個就是靜態鏈接。
可是靜態鏈接有個明顯的缺點,如果每個程序都需要printf,那麼printf這個函數的代碼就會同時存在在每個程序中,這樣也太佔地方了吧。所以發明了動態連接技術,其實有兩種形式。無論哪一種,都是首先記錄下需要調用printf這個函數以及所在的動態庫,等到運行的時候再載入動態庫,從動態庫中找到真正的printf去執行。
由於,動態鏈接技術需要一些額外的信息,傳統的靜態庫是不具備的,這些額外信息主要是重復載入和卸載時所需要的一些代碼,因此需要動態鏈接庫。
2. 靜態鏈接庫文件的問題
首先要搞明白 什麼是靜態庫,靜態庫是區別於動態庫而存在的,其目的說白了就是把代碼封裝成一個lib文件(注意,沒有DLL),在編譯的時候就載入進來使用,動態庫是在運行的時候才載入內容。
在使用另一個靜態庫的時候出了頭文件 庫文件lib還是得要的,不然編譯都通不過。
編譯之前必須載入靜態庫,跟動態庫一樣,也分靜態載入,和動態載入
3. 動態庫和靜態庫的區別
兩者區別:
a,靜態庫的使用需要:
1 包含一個對應的頭文件告知編譯器lib文件裡面的具體內容
2 設置lib文件允許編譯器去查找已經編譯好的二進制代碼
b,動態庫的使用:
程序運行時需要載入動態庫,對動態庫有依賴性,需要手動加入動態庫
c,依賴性:
靜態鏈接表示靜態性,在編譯鏈接之後, lib庫中需要的資源已經在可執行程序中了, 也就是靜態存在,沒有依賴性了
動態,就是實時性,在運行的時候載入需要的資源,那麼必須在運行的時候提供 需要的 動態庫,有依賴性, 運行時候沒有找到庫就不能運行了
d,區別:
簡單講,靜態庫就是直接將需要的代碼連接進可執行程序;動態庫就是在需要調用其中的函數時,根據函數映射表找到該函數然後調入堆棧執行。
做成靜態庫可執行文件本身比較大,但不必附帶動態庫
做成動態庫可執行文件本身比較小,但需要附帶動態庫
鏈接靜態庫,編譯的可執行文件比較大,當然可以用strip命令精簡一下(如:strip libtest.a),但還是要比鏈接動態庫的可執行文件大。程序運行時間速度稍微快一點。
靜態庫是程序運行的時候已經調入內存,不管有沒有調用,都會在內存里頭。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。
其在編譯程序時若鏈接,程序運行時會在系統指定的路徑下搜索,然後導入內存,程序一般執行時間稍微長一點,但編譯的可執行文件比較小;動態庫是程序運行的時候需要調用的時候才裝入內存,不需要的時候是不會裝入內存的。
動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。
動態鏈接庫的特點與優勢
首先讓我們來看一下,把庫函數推遲到程序運行時期載入的好處:
1. 可以實現進程之間的資源共享。
什麼概念呢?就是說,某個程序的在運行中要調用某個動態鏈接庫函數的時候,操作系統首先會查看所有正在運行的程序,看在內存里是否已有此庫函數的拷貝了。如果有,則讓其共享那一個拷貝;只有沒有才鏈接載入。這樣的模式雖然會帶來一些「動態鏈接」額外的開銷,卻大大的節省了系統的內存資源。C的標准庫就是動態鏈接庫,也就是說系統中所有運行的程序共享著同一個C標准庫的代碼段。
2. 將一些程序升級變得簡單。用戶只需要升級動態鏈接庫,而無需重新編譯鏈接其他原有的代碼就可以完成整個程序的升級。Windows 就是一個很好的例子。
3. 甚至可以真正坐到鏈接載入完全由程序員在程序代碼中控制。
程序員在編寫程序的時候,可以明確的指明什麼時候或者什麼情況下,鏈接載入哪個動態鏈接庫函數。你可以有一個相當大的軟體,但每次運行的時候,由於不同的操作需求,只有一小部分程序被載入內存。所有的函數本著「有需求才調入」的原則,於是大大節省了系統資源。比如現在的軟體通常都能打開若干種不同類型的文件,這些讀寫操作通常都用動態鏈接庫來實現。在一次運行當中,一般只有一種類型的文件將會被打開。所以直到程序知道文件的類型以後再載入相應的讀寫函數,而不是一開始就將所有的讀寫函數都載入,然後才發覺在整個程序中根本沒有用到它們。
靜態庫:在編譯的時候載入生成目標文件,在運行時不用載入庫,在運行時對庫沒有依賴性。
動態庫:在目標文件運行時載入,手動載入,且對庫有依賴性。
具體在開發中用到哪種庫,我覺得還是根據實際的內存大小,ROM大小,運行的速度等綜合考慮。
4. C語言編譯問題 靜態鏈接
鏈接靜態庫的意思就是在編譯階段需要將靜態庫鏈接到工程中,這樣在編譯時程序才不會出錯,在用到靜態庫的函數時能夠找到函數的實現,如果沒有鏈接靜態庫則編譯時會報錯。
這里需要區分的就是鏈接動態庫與靜態庫的區別,你可以到網上找一些資料看看。
而且鏈接靜態庫是將整個庫文件都導入到了工程中,所以會增加工程的大小。
5. linux下的靜態庫與動態庫的區別和使用
一、引言
我們通常把一些公用函數製作成函數庫,供其它程序使用。
函數庫分為靜態庫和動態庫兩種。
通常情況下,對函數庫的鏈接是放在編譯時期(compile time)完成的。所有相關的對象文件(object file)與牽涉到的函數庫(library)被鏈接合成一個可執行文件(executable file)。程序在運行時,與函數庫再無瓜葛,因為所有需要的函數已拷貝到相應目錄下下。所以這些函數庫被成為靜態庫(static libaray),通常文件名為「libxxx.a」的形式。
其實,我們也可以把對一些庫函數的鏈接載入推遲到程序運行的時期(runtime)。這就是動態鏈接庫(dynamic link library)技術。
二、兩者區別:
a,靜態庫的使用需要:
1 包含一個對應的頭文件告知編譯器lib文件裡面的具體內容
2 設置lib文件允許編譯器去查找已經編譯好的二進制代碼
b,動態庫的使用:
程序運行時需要載入動態庫,對動態庫有依賴性,需要手動加入動態庫
c,依賴性:
靜態鏈接表示靜態性,在編譯鏈接之後, lib庫中需要的資源已經在可執行程序中了, 也就是靜態存在,沒有依賴性了
動態,就是實時性,在運行的時候載入需要的資源,那麼必須在運行的時候提供 需要的 動態庫,有依賴性, 運行時候沒有找到庫就不能運行了
d,區別:
簡單講,靜態庫就是直接將需要的代碼連接進可執行程序;動態庫就是在需要調用其中的函數時,根據函數映射表找到該函數然後調入堆棧執行。
做成靜態庫可執行文件本身比較大,但不必附帶動態庫
做成動態庫可執行文件本身比較小,但需要附帶動態庫
鏈接靜態庫,編譯的可執行文件比較大,當然可以用strip命令精簡一下(如:strip libtest.a),但還是要比鏈接動態庫的可執行文件大。程序運行時間速度稍微快一點。
靜態庫是程序運行的時候已經調入內存,不管有沒有調用,都會在內存里頭。靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將不再需要該靜態庫。
其在編譯程序時若鏈接,程序運行時會在系統指定的路徑下搜索,然後導入內存,程序一般執行時間稍微長一點,但編譯的可執行文件比較小;動態庫是程序運行的時候需要調用的時候才裝入內存,不需要的時候是不會裝入內存的。
動態庫在程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入,因此在程序運行時還需要動態庫存在。
三、動態鏈接庫的特點與優勢
首先讓我們來看一下,把庫函數推遲到程序運行時期載入的好處:
1. 可以實現進程之間的資源共享。
什麼概念呢?就是說,某個程序的在運行中要調用某個動態鏈接庫函數的時候,操作系統首先會查看所有正在運行的程序,看在內存里是否已有此庫函數的拷貝了。如果有,則讓其共享那一個拷貝;只有沒有才鏈接載入。這樣的模式雖然會帶來一些「動態鏈接」額外的開銷,卻大大的節省了系統的內存資源。C的標准庫就是動態鏈接庫,也就是說系統中所有運行的程序共享著同一個C標准庫的代碼段。
2. 將一些程序升級變得簡單。用戶只需要升級動態鏈接庫,而無需重新編譯鏈接其他原有的代碼就可以完成整個程序的升級。Windows 就是一個很好的例子。
3. 甚至可以真正坐到鏈接載入完全由程序員在程序代碼中控制。
程序員在編寫程序的時候,可以明確的指明什麼時候或者什麼情況下,鏈接載入哪個動態鏈接庫函數。你可以有一個相當大的軟體,但每次運行的時候,由於不同的操作需求,只有一小部分程序被載入內存。所有的函數本著「有需求才調入」的原則,於是大大節省了系統資源。比如現在的軟體通常都能打開若干種不同類型的文件,這些讀寫操作通常都用動態鏈接庫來實現。在一次運行當中,一般只有一種類型的文件將會被打開。所以直到程序知道文件的類型以後再載入相應的讀寫函數,而不是一開始就將所有的讀寫函數都載入,然後才發覺在整個程序中根本沒有用到它們。
靜態庫:在編譯的時候載入生成目標文件,在運行時不用載入庫,在運行時對庫沒有依賴性。
動態庫:在目標文件運行時載入,手動載入,且對庫有依賴性。
具體在開發中用到哪種庫,我覺得還是根據實際的內存大小,ROM大小,運行的速度等綜合考慮。
6. 如何正確的使用靜態鏈接庫和動態鏈接庫
靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果採用靜態鏈接庫,則無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE
文件中了。但是若使用 DLL,該 DLL 不必被包含在最終 EXE 文件中,EXE 文件執行時可以「動態」地引用和卸載這個與 EXE 獨立的 DLL
文件。靜態鏈接庫和動態鏈接庫的另外一個區別在於靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫,而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接
庫。靜態鏈接庫與靜態鏈接庫調用規則總體比較如下。
對於靜態鏈接庫(比較簡單):
首先,靜態鏈接庫的使用需要庫的開發者提供生成庫的.h頭文件和.lib文件。
生成庫的.h頭文件中的聲明格式如下:
extern "C" 函數返回類型 函數名(參數表);
在調用程序的.cpp源代碼文件中如下:
#include "..\lib.h"
#pragma comment(lib,"..\\debug\\libTest.lib")
//指定與靜態庫一起鏈接
第二,因為靜態鏈接庫是將全部指令都包含入調用程序生成的EXE文件中。因此如果用的是靜態鏈接庫,那麼也就不存在「導出某個函數提供給用戶使用」的情況,要想用就得全要!要不就都別要!:)
對於動態鏈接庫:
動態鏈接庫的使用需要庫的開發者提供生成的.lib文件和.dll文件。或者只提供dll文件。
首先我們必須先注意到DLL內的函數分為兩種:
(1)DLL 導出函數,可供應用程序調用;
(2)DLL 內部函數,只能在 DLL 程序使用,應用程序無法調用它們。
因此調用程序若想調用DLL中的某個函數就要以某種形式或方式指明它到底想調用哪一個函數。
因此這里衍生出兩個問題:
第一:如何調用?即調用的方式
第二:不同調用方式對應的庫的生成過程和調用規則
我逐步展開問題。
第一,存在兩種調用方式——動態調用和靜態調用
第二,兩種調用方式的庫的生成過程和調用規則
1〉動態調用
生成庫的.h頭文件中的聲明格式如下:
extern "C" 函數返回類型 __declspec(dllexport) 函數名(參數表);
在調用程序的.cpp源代碼文件中按如下流程調用:
在main函數代碼的開始處
定義需要的DLL模塊的句柄和此DLL模塊模塊中需要調用的函數的函數指針。
定義好後緊接著就是標準的三部曲:
取得需要的DLL模塊------>返回模塊句柄
LoadLibrary("DLL模塊路徑")
取得需要的函數地址———>返回函數指針GetProcAddress(模塊句柄,"函數名")
從內存中卸載DLL模塊———>FreeLibrary(模塊句柄)
2>靜態調用
在調用程序的.cpp源代碼文件中按如下流程調用:
#include.........
告訴編譯器與 DLL 相對應的.lib 文件所在的路徑及文件名
#pragma comment(lib,"路徑和文件名")
extern "C" 函數返回類型 _declspec(dllimport) 函數名(參數表)分號
int main()
{
..............
}
7. 靜態鏈接與動態鏈接在鏈接過程和文件結構上的區別
動態鏈接庫和靜態鏈接庫一般是編譯集成一系列的介面(函數)
在程序源代碼編譯完成後通過編譯器編譯並通過鏈接器與這些庫進行鏈接
動態鏈接庫與靜態鏈接庫的區別在於鏈接器在進行鏈接時靜態庫會被直接編譯進程序里
而動態鏈接庫並不會,我們這里將這些鏈接庫稱作依賴(動態庫和靜態庫)
程序的運行需要這些依賴,程序在靜態鏈接後該程序本身便已包含該依賴
而動態鏈接後的程序本身本不包含該依賴,這些依賴需要執行者自行安裝進操作系統(動態庫、運行時庫)
程序運行時會動態地載入這些庫
linux上動態庫一般的後綴後為.so
靜態庫一般的後綴為.a
由於靜態鏈接會直接將庫編譯進程序里所以靜態編譯後的程序相較於動態鏈接所要大
這就是因為靜態鏈接會將鏈接庫編譯進程序里的原因,所以佔用就要大了
出於這種原因,靜態庫不易於維護與更新,如果鏈接庫中有實現有bug等需要更新則需要更新整個程序,因為靜態庫被編譯進程序中了
但動態庫就沒有這種情況了,因為動態庫是程序運行時動態載入的,所以我們只需要更新動態庫而不需要更新所有依賴該庫的程序(軟體)
另一方面,很多程序的開發都會使用到相同的鏈接庫,也就是很多程序(軟體)會有相同的依賴
如果將這些依賴全部靜態編譯的話將會造成存儲資源佔用過多而造成資源浪費
而使用動態庫的方式這些程序(軟體)則可以共享一個鏈接庫,而不需要每個程序都帶一個鏈接庫,這樣就大大地減少了存儲資源佔用空間
8. OpenCV靜態庫編譯與鏈接
以OpenCV-4.0.0為例說明在Ubuntu環境下的靜態庫編譯與鏈接方法:
opencv依賴第三方的庫,所以當把opencv編譯成靜態庫的時候,注意要把它運行時必要的依賴庫也編譯進來,opencv源碼內有自己的3rdparty,主要有:libjpeg、libpng、libtiff、libzlib、libwebp、libprotobuf、ffmpeg(用到video時編譯)、libgtk(用於顯示界面顯示,伺服器部署不會用到),可以通過cmake -LA來查看配置的編譯信息,再根據信息考慮把opencv的哪些mole編譯進來。
鏈接時指定opencv的靜態庫以及依賴的庫。另外需要注意頭文件的問題,通過CMakeLists把opencv加入項目編譯時,由於opencv編譯完成後要install才會把所用的頭文件集中拷貝到一個地方,所以事先將頭文件拷貝到了項目的opencv目錄下。
9. 為什麼要把SNMP++開發包編譯為靜態鏈接庫文件
靜態編譯:就是在編譯可執行文件的時候,將可執行文件需要調用的對應動態鏈接庫(.so)中的部分提取出來,鏈接到可執行文件中去,使可執行文件在運行的時候不依賴動態鏈接庫。
編譯方式:
第1種:
設置:
1、項目->配置屬性->常規->MFC的使用:在靜態庫中使用MFC
2、項目 -> 配置屬性->C/C++->代碼生成->運行庫 :選擇 多線程調試(/MTd)。
編譯時,選擇的是debug,win32
然後執行編譯生成方案,在該工程目錄下的debug文件中,找到該.exe文件,即可在其他電腦運行。
第2種:
一般可以配置一下兩項:
1.項目 -> 配置屬性->常規->MFC的使用 :在靜態庫中使用MFC。
2.項目 -> 配置屬性->C/C++->代碼生成->運行庫 :選擇 多線程調試(/MT)。
編譯時,選擇的是release,win32(這個選擇項在工具欄的debug選框中,一般我們使用debug方式)
10. 編譯原理-編譯靜態庫並鏈接
編譯成.o 文件(目標文件)只需要包含頭文件,並且能找到頭文件路徑即可
之後需要把編譯好的 TestExample.o 名稱修改為 libTestExample.a ,直接當做靜態庫使用(靜態庫.a文件實際上是.o文件的合集)
或者使用下面的命令把.o打包成靜態庫
然後開始鏈接成可執行文件,mach-o文件
鏈接成功後,執行mach-o
看到列印,運行成功!
使用objmp 命令查看
整個編譯過程腳本:
