Linux 靜態庫和動態庫

? ? ? ? 不管是Linux還是Windows中的庫文件其本質和工作模式都是相同的, 只不過在不同的平臺上庫對應的文件格式和文件后綴不同。程序中調用的庫有兩種 靜態庫和動態庫,不管是哪種庫文件本質是還是源文件,只不過是二進制格式只有計算機能夠識別,作為一個普通人就無能為力了。

????????在項目中使用庫一般有兩個目的,一個是為了使程序更加簡潔不需要在項目中維護太多的源文件,另一方面是為了源代碼保密,畢竟不是所有人都想把自己編寫的程序開源出來。

????????當我們拿到了庫文件(動態庫、靜態庫)之后要想使用還必須有這些庫中提供的API函數的聲明,也就是頭文件,把這些都添加到項目中,就可以快樂的寫代碼了。

1. 靜態庫

????????在Linux中靜態庫由程序 ar 生成,現在靜態庫已經不像之前那么普遍了,這主要是由于程序都在使用動態庫。關于靜態庫的命名規則如下:

  • 在Linux中靜態庫以lib作為前綴, 以.a作為后綴, 中間是庫的名字自己指定即可, 即: libxxx.a
  • 在Windows中靜態庫一般以lib作為前綴, 以lib作為后綴, 中間是庫的名字需要自己指定, 即: libxxx.lib

1.1 生成靜態鏈接庫

????????生成靜態庫,需要先對源文件進行匯編操作 (使用參數 -c) 得到二進制格式的目標文件 (.o 格式), 然后在通過 ar工具將目標文件打包就可以得到靜態庫文件了 (libxxx.a)。

使用ar工具創建靜態庫的時候需要三個參數:

  • 參數c:創建一個庫,不管庫是否存在,都將創建。
  • 參數s:創建目標文件索引,這在創建較大的庫時能加快時間。
  • 參數r:在庫中插入模塊(替換)。默認新的成員添加在庫的結尾處,如果模塊名已經在庫中存在,則替換同名的模塊。

生成靜態鏈接庫的具體步驟如下:

????????1.需要將源文件進行匯編, 得到 .o 文件, 需要使用參數 -c????????

# 執行如下操作, 默認生成二進制的 .o 文件
# -c 參數位置沒有要求
$ gcc 源文件(*.c) -c	

????????2.將得到的 .o 進行打包, 得到靜態庫

$ ar rcs 靜態庫的名字(libxxx.a) 原材料(*.o)

? ? ? ? 3.發布靜態庫

# 發布靜態庫1. 提供頭文件 **.h2. 提供制作出來的靜態庫 libxxx.a

1.2 靜態庫制作舉例

1.2.1 準備測試程序

在某個目錄中有如下的源文件, 用來實現一個簡單的計算器:

# 目錄結構 add.c div.c mult.c sub.c -> 算法的源文件, 函數聲明在頭文件 head.h
# main.c中是對接口的測試程序, 制作庫的時候不需要將 main.c 算進去
.
├── add.c
├── div.c
├── include
│?? └── head.h
├── main.c
├── mult.c
└── sub.c

加法計算源文件 add.c:

#include <stdio.h>
#include "head.h"int add(int a, int b)
{return a+b;
}

減法計算源文件 sub.c:

#include <stdio.h>
#include "head.h"int subtract(int a, int b)
{return a-b;
}

乘法計算源文件 mult.c:

#include <stdio.h>
#include "head.h"int multiply(int a, int b)
{return a*b;
}

減法計算的源文件 div.c

#include <stdio.h>
#include "head.h"double divide(int a, int b)
{return (double)a/b;
}

頭文件 head.h

#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 減法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif

測試文件main.c

#include <stdio.h>
#include "head.h"int main()
{int a = 20;int b = 12;printf("a = %d, b = %d\n", a, b);printf("a + b = %d\n", add(a, b));printf("a - b = %d\n", subtract(a, b));printf("a * b = %d\n", multiply(a, b));printf("a / b = %f\n", divide(a, b));return 0;
}

1.2.2 生成靜態庫

????????第一步: 將源文件add.c, div.c, mult.c, sub.c 進行匯編, 得到二進制目標文件 add.o, div.o, mult.o, sub.o

# 1. 生成.o
$ gcc add.c div.c mult.c sub.c -c
sub.c:2:18: fatal error: head.h: No such file or directory
compilation terminated.# 提示頭文件找不到, 添加參數 -I 重新頭文件路徑即可
$ gcc add.c div.c mult.c sub.c -c -I ./include/# 查看目標文件是否已經生成
$ tree
.
├── add.c
├── add.o            # 目標文件
├── div.c
├── div.o            # 目標文件
├── include
│?? └── head.h
├── main.c
├── mult.c
├── mult.o           # 目標文件
├── sub.c
└── sub.o            # 目標文件

第二步: 將生成的目標文件通過 ar工具打包生成靜態庫

# 2. 將生成的目標文件 .o 打包成靜態庫
$ ar rcs libcalc.a a.o b.o c.o    # a.o b.o c.o在同一個目錄中可以寫成 *.o# 查看目錄中的文件
$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── include
│?? └── `head.h  ===> 和靜態庫一并發布
├── `libcalc.a   ===> 生成的靜態庫
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o

第三步: 將生成的的靜態庫 libcalc.a和庫對應的頭文件head.h一并發布給使用者就可以了

# 3. 發布靜態庫1. head.h    => 函數聲明2. libcalc.a => 函數定義(二進制格式)

1.3 靜態庫的使用

????????當我們得到了一個可用的靜態庫之后, 需要將其放到一個目錄中, 然后根據得到的頭文件編寫測試代碼, 對靜態庫中的函數進行調用。

# 1. 首先拿到了發布的靜態庫`head.h` 和 `libcalc.a`# 2. 將靜態庫, 頭文件, 測試程序放到一個目錄中準備進行測試
.
├── head.h          # 函數聲明
├── libcalc.a       # 函數定義(二進制格式)
└── main.c          # 函數測試

編譯測試程序, 得到可執行文件。

# 3. 編譯測試程序 main.c
$ gcc main.c -o app
/tmp/ccR7Fk49.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status

上述錯誤分析:

????????編譯的源文件中包含了頭文件 head.h, 這個頭文件中聲明的函數對應的定義(也就是函數體實現)在靜態庫中,程序在編譯的時候沒有找到函數實現,因此提示 undefined reference to xxxx。

解決方案:在編譯的時將靜態庫的路徑和名字都指定出來

  • -L: 指定庫所在的目錄(相對或者絕對路徑)
  • -l: 指定庫的名字, 需要掐頭(lib)去尾(.a) 剩下的才是需要的靜態庫的名字
# 4. 編譯的時候指定庫信息-L: 指定庫所在的目錄(相對或者絕對路徑)-l: 指定庫的名字, 掐頭(lib)去尾(.a) ==> calc
# -L -l, 參數和參數值之間可以有空格, 也可以沒有  -L./ -lcalc
$ gcc main.c -o app -L ./ -l calc# 查看目錄信息, 發現可執行程序已經生成了
$ tree
.
├── app   		# 生成的可執行程序
├── head.h
├── libcalc.a
└── main.c

2. 動態庫

????????動態鏈接庫是程序運行時加載的庫,當動態鏈接庫正確部署之后,運行的多個程序可以使用同一個加載到內存中的動態庫,因此在Linux中動態鏈接庫也可稱之為共享庫。

????????動態鏈接庫是目標文件的集合,目標文件在動態鏈接庫中的組織方式是按照特殊方式形成的。庫中函數和變量的地址使用的是相對地址(靜態庫中使用的是絕對地址),其真實地址是在應用程序加載動態庫時形成的。

關于動態庫的命名規則如下:

  • 在Linux中動態庫以lib作為前綴, 以.so作為后綴, 中間是庫的名字自己指定即可, 即: libxxx.so
  • 在Windows中動態庫一般以lib作為前綴, 以dll作為后綴, 中間是庫的名字需要自己指定, 即: libxxx.dll

2.1 生成動態鏈接庫

????????生成動態鏈接庫是直接使用gcc命令并且需要添加-fPIC(-fpic) 以及-shared 參數。

  • -fPIC 或 -fpic 參數的作用是使得 gcc 生成的代碼是與位置無關的,也就是使用相對位置。
  • -shared參數的作用是告訴編譯器生成一個動態鏈接庫。

生成動態鏈接庫的具體步驟如下:

? ? ? ? 1、將源文件進行匯編操作, 需要使用參數 -c, 還需要添加額外參數 -fpic / -fPIC

# 得到若干個 .o文件
$ gcc 源文件(*.c) -c -fpic

? ? ? ?2、 將得到的.o文件打包成動態庫, 還是使用gcc, 使用參數 -shared 指定生成動態庫(位置沒有要求)

$ gcc -shared 與位置無關的目標文件(*.o) -o 動態庫(libxxx.so)

? ? ? ? 3、發布動態庫和頭文件

# 發布1. 提供頭文件: xxx.h2. 提供動態庫: libxxx.so

2.2 動態庫制作舉例

????????在此還是以上面制作靜態庫使用的實例代碼為例來制作動態庫, 代碼目錄如下:

# 舉例, 示例目錄如下:
# 目錄結構 add.c div.c mult.c sub.c -> 算法的源文件, 函數聲明在頭文件 head.h
# main.c中是對接口的測試程序, 制作庫的時候不需要將 main.c 算進去
.
├── add.c
├── div.c
├── include
│?? └── head.h
├── main.c
├── mult.c
└── sub.c

第一步: 使用gcc將源文件進行匯編(參數-c), 生成與位置無關的目標文件, 需要使用參數 -fpic或者-fPIC

# 1. 將.c匯編得到.o, 需要額外的參數 -fpic/-fPIC
$ gcc add.c div.c mult.c sub.c -c -fpic -I ./include/# 查看目錄文件信息, 檢查是否生成了目標文件
$ tree
.
├── add.c
├── add.o                # 生成的目標文件
├── div.c
├── div.o                # 生成的目標文件
├── include
│?? └── head.h
├── main.c
├── mult.c
├── mult.o               # 生成的目標文件
├── sub.c
└── sub.o                # 生成的目標文件

第二步: 使用gcc將得到的目標文件打包生成動態庫, 需要使用參數 -shared

# 2. 將得到 .o 打包成動態庫, 使用gcc , 參數 -shared
$ gcc -shared add.o div.o mult.o sub.o -o libcalc.so  # 檢查目錄中是否生成了動態庫
$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── include
│?? └── `head.h   ===> 和動態庫一起發布
├── `libcalc.so   ===> 生成的動態庫
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o

第三步: 發布生成的動態庫和相關的頭文件

# 3. 發布庫文件和頭文件1. head.h2. libcalc.so

2.3 動態庫的使用

????????當我們得到了一個可用的動態庫之后, 需要將其放到一個目錄中, 然后根據得到的頭文件編寫測試代碼, 對動態庫中的函數進行調用。

# 1. 拿到發布的動態庫`head.h   libcalc.so
# 2. 基于頭文件編寫測試程序, 測試動態庫中提供的接口是否可用`main.c`
# 示例目錄:
.
├── head.h          ==> 函數聲明
├── libcalc.so      ==> 函數定義
└── main.c          ==> 函數測試

編譯測試程序

# 3. 編譯測試程序
$ gcc main.c -o app
/tmp/ccwlUpVy.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status

錯誤原因:

  • 和使用靜態庫一樣, 在編譯的時候需要指定庫相關的信息: 庫的路徑 -L和 庫的名字 -l

添加庫信息相關參數, 重新編譯測試代碼:

# 在編譯的時候指定動態庫相關的信息: 庫的路徑 -L, 庫的名字 -l
$ gcc main.c -o app -L./ -lcalc# 查看是否生成了可執行程序
$ tree
.
├── app 			# 生成的可執行程序
├── head.h
├── libcalc.so
└── main.c# 執行生成的可執行程序, 錯誤提示 ==> 可執行程序執行的時候找不到動態庫
$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

3. 優缺點

3.1 靜態庫

優點:

  • 靜態庫被打包到應用程序中加載速度快
  • 發布程序無需提供靜態庫,移植方便

缺點:

  • 相同的庫文件數據可能在內存中被加載多份, 消耗系統資源,浪費內存
  • 庫文件更新需要重新編譯項目文件, 生成新的可執行程序, 浪費時間。

3.2 動態庫

優點:

  • 可實現不同進程間的資源共享
  • 動態庫升級簡單, 只需要替換庫文件, 無需重新編譯應用程序
  • 程序猿可以控制何時加載動態庫, 不調用庫函數動態庫不會被加載

缺點:

  • 加載速度比靜態庫慢, 以現在計算機的性能可以忽略
  • 發 布程序需要提供依賴的動態庫

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/38339.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/38339.shtml
英文地址,請注明出處:http://en.pswp.cn/web/38339.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Node-RED 4.0.2】4.0版本新增特性(官方版)

二、重要功能 *1.時間戳格式改進 過去&#xff0c;node-red 只提供了 最原始的 timestamp 的格式&#xff08;1970-01-01 ~ now&#xff09; 但是現在&#xff0c;額外增加了 2 種格式&#xff1a; ISO 8601 -A COMMON FORMAT&#xff08;YYYY-MM-DDTHH:mm:ss:sssZ&#xff…

思考如何學習一門編程語言?

一、什么是編程語言 編程語言是一種用于編寫計算機程序的人工語言。通過編程語言&#xff0c;程序員可以向計算機發出指令&#xff0c;控制計算機執行各種任務和操作。編程語言由一組語法規則和語義規則組成&#xff0c;這些規則定義了如何編寫代碼以及代碼的含義。 編程語言…

linux和mysql基礎指令

Linux中nano和vim讀可以打開記事文件。 ifdown ens33 ifup ens33 關閉&#xff0c;開啟網絡 rm -r lesson1 gcc -o code1 code1.c 編譯c語言代碼 ./code1 執行c語言代碼 rm -r dir 刪除文件夾 mysql> show databases-> ^C mysql> show databases; -------…

常見網絡端口號

在網絡工程領域&#xff0c;了解和掌握默認端口號是至關重要的。端口號是計算機網絡中最基本的概念之 一&#xff0c;用于標識特定的網絡服務或應用程序。 1、什么是端口號&#xff1f; 端口號是計算機網絡中的一種標識&#xff0c;用于區分不同的網絡服務和應用程序。每個端…

【C++進階學習】第五彈——二叉搜索樹——二叉樹進階及set和map的鋪墊

二叉樹1&#xff1a;深入理解數據結構第一彈——二叉樹&#xff08;1&#xff09;——堆-CSDN博客 二叉樹2&#xff1a;深入理解數據結構第三彈——二叉樹&#xff08;3&#xff09;——二叉樹的基本結構與操作-CSDN博客 二叉樹3&#xff1a;深入理解數據結構第三彈——二叉樹…

想要打造超高性能的接口API?試試這12條小技巧。

1. 并行處理 簡要說明 舉個例子&#xff1a;在價格查詢鏈路中&#xff0c;我們需要獲取多種獨立的價格配置項信息&#xff0c;如基礎價、折扣價、商戶活動價、平臺活動價等等。 CompletableFuture 是銀彈嗎&#xff1f; 使用 CompletableFuture 的確能夠幫助我們解決許多獨…

走進IT的世界

引言 隨著高考的結束&#xff0c;對于即將踏入IT&#xff08;信息技術&#xff09;領域的新生而言&#xff0c;這個假期不僅是放松身心的時間&#xff0c;更是提前規劃、深化專業知識、為大學生活奠定堅實基礎的寶貴機會。以下是一份詳盡的高考假期預習與規劃指南&#xff0c;…

Android自動化測試實踐:uiautomator2 核心功能與應用指南

Android自動化測試實踐&#xff1a;uiautomator2 核心功能與應用指南 uiautomator2 是一個用于Android應用的自動化測試Python庫&#xff0c;支持多設備并行測試操作。它提供了豐富的API來模擬用戶對App的各種操作&#xff0c;如安裝、卸載、啟動、停止以及清除應用數據等。此外…

30個!2024重大科學問題、工程技術難題和產業技術問題發布

【SciencePub學術】中國科協自2018年開始&#xff0c;組織開展重大科技問題難題征集發布活動&#xff0c;引導廣大科技工作者緊跟世界科技發展大勢&#xff0c;聚焦國家重大需求&#xff0c;開展原創性、引領性研究&#xff0c;不斷夯實高質量發展的科技支撐。 自2024年征集活動…

飛書文檔轉markdown 超級快捷方法。

直接使用那個github的高贊官方的工具轉換&#xff0c;需要設置什么小應用那種東西&#xff0c;還要審批&#xff0c;社恐人表示怕了怕了。而且文檔我分享出去&#xff0c;是有權限的&#xff0c;反正無論如何生成不了 我的方法是 直接全選&#xff0c;然后粘貼進Arya - 在線 …

C#的五大設計原則-solid原則

什么是C#的五大設計原則&#xff0c;我們用人話來解釋一下&#xff0c;希望小伙伴們能學會&#xff1a; 好的&#xff0c;讓我們以一種幽默的方式來解釋C#的五大設計原則&#xff08;SOLID&#xff09;&#xff1a; 單一職責原則&#xff08;Single Responsibility Principle…

PCL 漸進形態過濾器實現地面分割

點云地面分割 一、代碼實現二、結果示例?? 概述 漸進形態過濾器:采用先腐蝕后膨脹的運算過程,可以有效濾除場景中的建筑物、植被、車輛、行人以及交通附屬設施,保留道路路面及路緣石點云。 一、代碼實現 #include <iostream> #include <pcl/io/pcd_io.h> #in…

【LeetCode】976. 三角形的最大周長

1. 題目 2. 分析 需要分析好再動手編程。 如果要構成三角形的最大周長&#xff0c;那么就需要盡可能用最長的邊構建。所以可以先對數組排個序&#xff0c;然后基于排序得到的結果從大往小的逐個檢查長度為3的窗口&#xff0c;判斷該窗口的值是否滿足三角形的構成條件&#x…

鴻蒙開發Ability Kit(程序訪問控制):【安全控件概述】

安全控件概述 安全控件是系統提供的一組系統實現的ArkUI組件&#xff0c;應用集成這類組件就可以實現在用戶點擊后自動授權&#xff0c;而無需彈窗授權。它們可以作為一種“特殊的按鈕”融入應用頁面&#xff0c;實現用戶點擊即許可的設計思路。 相較于動態申請權限的方式&am…

構造,析構,拷貝【類和對象(中)】

P. S.&#xff1a;以下代碼均在VS2019環境下測試&#xff0c;不代表所有編譯器均可通過。 P. S.&#xff1a;測試代碼均未展示頭文件stdio.h的聲明&#xff0c;使用時請自行添加。 博主主頁&#xff1a;LiUEEEEE ??????????????????? ?? …

Excel_VBA編程

在Excel中&#xff0c;VBA&#xff08;Visual Basic for Applications&#xff09;是一種強大的工具&#xff0c;可以用來自動化各種任務。下面介紹一些常用的VBA函數和程序結構&#xff1a; 常用函數 MsgBox&#xff1a;用于顯示消息框。 MsgBox "Hello, World!"In…

【python全棧系列】day07-python數據類型-集合

Python中的集合&#xff08;Set&#xff09;是一個無序的、不包含重復元素的數據結構。它主要用于數學上的集合操作&#xff0c;如并集、交集、差集和對稱差集等。集合的基本用途包括去重和關系測試。 1、集合的特性 無序性&#xff1a;集合中的元素是無序的&#xff0c;這意…

gin-vue -admin 初始化安裝后 進入 后臺首頁報錯

報錯原因&#xff1a; 因為 我是使用的phpstudy 小皮的數據庫 默認的是MySam 的引擎 mysql 引擎需要是 innoDB 解決辦法 &#xff1a; 在linux 的環境下 配置一個數據庫 &#xff0c; 我是用的是vmware 虛擬機

深入理解分布式搜索引擎 ElasticSearch,并能基于 ELK+Kafka 搭建分布式?志收集系統

Elasticsearch是一個基于Lucene的分布式、多租戶能力的全文搜索引擎。它提供了RESTful web接口和分布式多用戶能力的全文搜索引擎&#xff0c;基于Apache許可證發行。以下是對Elasticsearch的深入理解以及如何基于ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;加…

npm緩存深度解析:理解、使用與清除指南

&#x1f31f; npm緩存深度解析&#xff1a;理解、使用與清除指南 npm&#xff08;Node Package Manager&#xff09;是JavaScript編程語言的包管理器&#xff0c;廣泛用于Node.js應用程序。它不僅幫助我們安裝和管理項目依賴&#xff0c;還擁有一個強大的緩存機制來加速這一過…