C 語言編程 — 靜態鏈接庫和動態鏈接庫

目錄

C 語言的庫

C 語言的庫有兩種,靜態鏈接庫和動態鏈接庫。在 Linux 中,靜態鏈接庫命名爲 lib*.a,動態庫爲 lib*.so。這些庫文件存在的價值就是抽象出通用的函數,避免重複造輪子。不管是使用哪一種庫,程序員必須在程序中通過 include 來包含相應的頭文件,並在預編譯階段替換 include 的內容,然後在鏈接階段將調用到的庫函數從各自所在的檔案庫中鏈接到合適的地方。

在這裏插入圖片描述

靜態鏈接庫

靜態鏈接,即:在鏈接階段,將源文件中用到的庫函數與彙編生成的 .o 目標文件 合併生成 可執行文件。所以,靜態鏈接方式的好處是:方便程序移植,因爲可執行程序包含了所有庫函數的內容,放在任何環境當中都可以執行。缺點就是:可執行文件通常會比較大。而且每次庫文件升級的話,都要重新編譯源文件,很不方便。

直觀的看,一個全靜態方式生成的簡單 print 程序大小爲 857K,而動態鏈接生成的一樣的可執行文件只有 8.4K,因爲靜態鏈接的可執行文件包含了整理 stdio 庫文件。

在這裏插入圖片描述
如上圖,對於靜態編譯的程序 1、2,因爲都使用了 staticMath 庫。所以在內存中就有兩份相同的 staticMath.o 目標文件,一旦程序數量過多就很可能會內存不足,很浪費空間。

生成靜態鏈接庫

  • add.c
#include "add.h"

int add(int a, int b) {
    return a + b;
}
  • add.h
#ifndef _ADD_H
#define _ADD_H

int add(int a, int b);

#endif
  • 生成目標文件
gcc -c add.c --std c99
  • 生成靜態鏈接庫文件
ar -crv libadd.a add.o

使用靜態鏈接庫:

  1. include
#include <stdio.h>

#include "./add.h"


int main() {
     int number1 = 10;
     int number2 = 90;
     printf("SUM: %d\n", add(number1, number2));
     return 0;
}
  1. 編譯
gcc -std=c99 test.c -o test -L./ -ladd
  • -L:指定加載庫文件的路徑。
  • -l:指定加載的庫文件。

動態鏈接庫

動態鏈接,即:在程序運行過程中動態的調用庫文件。好處是:佔空間小、程序文件小。缺點是:可移植性太差,如果兩臺電腦運行環境不同,例如:動態庫存放的位置不一樣、沒有動態庫文件,就很可能導致程序運行失敗。

在這裏插入圖片描述

生成動態鏈接庫

  • 生成目標文件
gcc -c add.c --std c99
  • 生成動態鏈接庫文件
    • -shared :指定生成動態鏈接庫。
    • -fPIC :表示編譯爲位置獨立的代碼,用於編譯共享庫。目標文件需要創建成位置無關碼,就是在可執行程序裝載它們的時候,它們可以放在可執行程序的內存裏的任何地方。
gcc -fPIC -shared -o libadd.so add.c
  • 編譯並動態鏈接庫文件
gcc -std=c99 test.c -o test -L./ -ladd
  • 執行程序
$ ./test
./test: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory

ERR:libadd.so: cannot open shared object file: No such file or directory

因爲執行程序找不到 libadd.so,查看 test 程序的動態鏈接庫信息:

$ ldd test
	linux-vdso.so.1 =>  (0x00007fff39fd5000)
	libadd.so => not found
	libc.so.6 => /lib64/libc.so.6 (0x00007f5410c0b000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f5410fd9000)

可以看到 test 執行程序用到的 libadd.so 確實是 not found,這是因爲在 /etc/ld.so.conf 文件中設置了動態鏈接庫了尋找路徑:

$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf

$ ll /etc/ld.so.conf.d
總用量 40
-rw-r--r--  1 root root 26 4月   7 22:41 bind-export-x86_64.conf
-rw-r--r--  1 root root 24 4月   2 02:32 hcoll.conf
-rw-r--r--  1 root root 19 4月   2 02:23 ibutils.conf
-r--r--r--  1 root root 63 4月   1 07:40 kernel-3.10.0-1127.el7.x86_64.conf
-r--r--r--. 1 root root 63 10月 21 2017 kernel-3.10.0-693.5.2.el7.x86_64.conf
-r--r--r--  1 root root 63 11月 29 2018 kernel-3.10.0-957.1.3.el7.x86_64.conf
-r--r--r--  1 root root 63 5月   1 22:57 kernel-rt-3.10.0-1127.rt56.1093.el7.x86_64.conf
-rw-r--r--  1 root root 17 4月   3 01:52 mariadb-x86_64.conf
-rw-r--r--  1 root root 22 4月   2 02:26 mxm.conf
-rw-r--r--  1 root root 24 4月   2 02:28 sharp.conf

顯然這裏是沒有 libadd.so 的存儲路徑的,所以我們需要添加一下 libadd.so 的路徑:

$ cat /etc/ld.so.conf.d/test.conf
/root/workspace/test

然後執行 ldconfig 命令生效,再次執行 test 程序:

$ ./test
SUM: 100

總結

綜上說述,靜態和動態鏈接庫的選擇要視情況而定。通常推薦動態鏈接方式,因爲可以很好的節約內存,而且方便後續庫文件的升級。

參考文檔

https://www.cnblogs.com/52php/p/5681711.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章