《電話號碼管理系統》制作成靜態庫和動態庫
一、前言
上次寫了一篇關于Makefile的文章。
《利用Makfile給多文件、多目錄C源碼建立工程》
有很多粉絲留言,有的粉絲想進一步了解cmake的使用方法,還有的粉絲想知道如何將一些函數編譯成動態庫或者靜態庫,然后再將該庫編譯到內存中。
一口君必須安排,本篇先講如何將一些函數編譯成動態庫或者靜態庫。
這就涉及到一個庫的概念,關于制作的庫的基礎知識,一口君已經在下面這篇文章中詳細的講述了相關概念,建議大家先看下面這篇文章。
《Linux庫概念,動態庫和靜態庫概念》
本文,一口君將繼續以之前的 電話號碼管理系統的項目為基礎,給大家詳細講解如何將該項目中的函數制作成動態庫和靜態庫。
《從0寫一個《電話號碼管理系統》的C入門項目》
二、 基礎知識
1) 靜態庫
所謂靜態庫,就是在靜態編譯時由編譯器到指定目錄尋找并且進行鏈接,一旦鏈接完成,最終的可執行程序中就包含了該庫文件中的所有有用信息,包括代碼段、數據段等。
2)動態庫
所謂動態庫,就是在應用程序運行時,由操作系統根據應用程序的請求,動態到指定目錄下尋找并裝載入內存中,同時需要進行地址重定向。
3)庫文件命名
靜態庫的名字一般為libxxxx.a,其中xxxx是該lib的名稱;動態庫的名字一般為libxxxx.so.x.y.z,含義如下圖所示:
4)制作庫文件常用參數
首先需要了解gcc編譯庫要用到一些參數,很重要。
三、 制作靜態庫
原始文件目錄如下:
- peng@ubuntu:/mnt/hgfs/code/phone3$ tree .
- .
- ├── main.c
- ├── phone.c
- └── phone.h
- 0 directories, 3 files
其中 phone.c包含了對鏈表的所有的操作函數 phone.h 是phone.c中所有函數的原型說明 main.c是主程序
下面我們將phone.c制作成靜態庫。
1. 把 listd.c 編譯成.o文件
- peng@ubuntu:/mnt/hgfs/code/phone3$ gcc -c phone.c
2. 使用 ar 命令生成靜態庫libadd.a
靜態庫名字遵循靜態庫命名的規則 lib + 名字 + .a
peng@ubuntu:/mnt/hgfs/code/phone3$ ar -rc libphone.a phone.o
3. 將庫和頭文件拷貝到其他目錄下
將庫文件移動到lib目錄下
- peng@ubuntu:/mnt/hgfs/code/phone3$ mkdir lib
- peng@ubuntu:/mnt/hgfs/code/phone3$ mv libphone.a lib
移動頭文件到include目錄下
- peng@ubuntu:/mnt/hgfs/code/phone3$ mkdir include
- peng@ubuntu:/mnt/hgfs/code/phone3$ mv phone.h include/
刪除phone.c
- peng@ubuntu:/mnt/hgfs/code/phone3$ rm phone.c
此處可不刪除,下面的的編譯已經用不到該文件 刪除僅僅是為了排除干擾,有些同學會以為這個文件還會被編譯進去
最終文件結構如下:
- peng@ubuntu:/mnt/hgfs/code/phone3$ tree ./
- ./
- ├── include
- │ └── phone.h
- ├── lib
- │ └── libphone.a
- ├── main.c
- └── run
- 2 directories, 6 files
lib include 目錄也可以是其他目錄,實際項目中庫文件和頭文件都會放到一些指定目錄下
4.編譯
值編譯main.c,會有以下錯誤提示,主要是因為phone.h
- peng@ubuntu:/mnt/hgfs/code/phone3$ gcc main.c
- main.c:3:19: 致命錯誤:phone.h:沒有那個文件或目錄
- 編譯中斷。
制定頭文件位置,編譯結果如下,可以看到錯誤提示,“沒有定義create”,這是因為在鏈接的時候找打不到這些函數的定義的地方
- peng@ubuntu:/mnt/hgfs/code/phone3$ gcc main.c -I ./include
- /tmp/cctUUKm9.o: In function `management':
- main.c:(.text+0x109): undefined reference to `create'
- main.c:(.text+0x120): undefined reference to `delete'
- main.c:(.text+0x137): undefined reference to `search'
- main.c:(.text+0x14e): undefined reference to `display'
- main.c:(.text+0x167): undefined reference to `allfree'
- /tmp/cctUUKm9.o: In function `main':
- main.c:(.text+0x2e3): undefined reference to `init'
- collect2: ld 返回 1
最終我們執行
- peng@ubuntu:/mnt/hgfs/code/phone3$ gcc main.c -I ./include ./lib/libphone.a
指定了頭文件和庫文件位置,執行結果如下:
與之前運行現象是一樣的。
可見,使用庫的時候我們必須制定頭文件目錄以及庫目錄。
四、 制作動態庫原始文件
- peng@ubuntu:/mnt/hgfs/code/phone3$ tree .
- .
- ├── main.c
- ├── phone.c
- └── phone.h
- 0 directories, 3 files
1. 把phone.c編譯成動態鏈接庫libphone.so
- gcc -fPIC -o libphone.o -c phone.c
- gcc -shared -o libphone.so libphone.o
也可以直接使用一條命令
- gcc -fPIC -shared -o libphone.so phone.c
2. 動態庫的安裝
通常動態庫拷貝到/lib下:
- peng@ubuntu:/mnt/hgfs/code/phone4$ sudo mv libphone.so /lib/
- [sudo] password for peng:
刪除phone.c
- peng@ubuntu:/mnt/hgfs/code/phone3$ rm phone.c
3. 編譯執行
編譯動態庫:
- peng@ubuntu:/mnt/hgfs/code/phone4$ gcc main.c -lphone -o run
此時使用我們制作的動態庫,只需要加上 -lphone即可
- 注意觀察編譯時動態庫的名字與庫文件對應關系
- libphone.so<--------->-lphone
執行結果如下:
五、重新建立工程
下面我們將文件重新放置
當前文件目錄如下:
- ./include
- └── phone.h
- ./Makefile
- ./obj
- └── Makefile
- ./src
- ├── main.c
- └── Makefile
- 0 directories, 5 files
并添加3個Makefile
編譯步驟如下:
聲明環境變量
- CC 編譯名稱
- LIBS 用到的動態庫
- SUBDIRS 子目錄
- OBJS src下所有的目標文件
- BIN 最終生成的可執行程序名字
- OBJS_DIR 目標文件存放目錄
- BIN_DIR 可執行程序存儲目錄
執行make的默認目標all,依賴CHECK_DIR $(SUBDIRS)
執行目標CHECK_DIR ,創建目錄bin
執行目標@ ,進入子目錄src、obj執行子目錄的Makefile,
打印語句 echo begin compile phone!
進入子目錄src執行Makfile,
執行命令
- @$(CC) -c main.c -I../include -o ../$(OBJS_DIR)/main.o
- @ :打印該條命令
- -I../include 頭文件在上一級目錄下的include中
- -o ../$(OBJS_DIR)/main.o 生成的目標文件存放在../obj/main.o
進入子目錄obj執行Makfile, 目標為../bin/phone:main.o 執行命令
- @$(CC) -o $@ $^ $(LIBS)
- @$(CC) 同上
- $@ 表示生成的目標文件,即../bin/phone
- $^ 表示所有的依賴文件,即上面:后面目標文件main.o
編譯完成后就會在bin目錄下創建可執行程序文件phone, 運行結果如下:
本文轉載自微信公眾號「一口Linux」