Linux下动静态库的制作与使用
文章目录
- 一 :star2:动静态库介绍
- :dizzy: 静态库
- :dizzy: 动态库(共享库)
- 二 . :crescent_moon: 静态库的制作和使用
- :sparkles:静态库的制作
- 1. :sparkles: **编写源文件**
- 2. :sparkles:**编译生成目标文件**
- 3. :sparkles:**创建静态库**
- :key: 静态库的使用
- :key: 1. 编写调用静态库的源文件,和提供声明的头文件
- :key: 2. 编译链接静态库
- :key: 3. 运行可执行文件
- 三 :balloon:动态库的创建和使用
- :rocket:动态库的制作
- :rocket:1. 编写源文件
- :rocket:2. 生成目标文件
- :rocket:3. 创建动态库
- :trident: 动态库的使用
- :trident: 1. 编写调用动态库的源文件,和提供声明的头文件
- :trident: 2. 编译链接动态库
- :trident: 3. 运行可执行文件

一 🌟动静态库介绍
💫 静态库
- 链接方式:在编译链接阶段,链接器将静态库中被程序调用的函数和数据,完整地复制到可执行文件中。一旦链接完成,可执行文件与静态库就不再有依赖关系,运行时无需静态库支持。
- 文件命名:遵循特定命名规则,通常以
lib
为前缀,以.a
作为后缀,如libexample.a
。
💫 动态库(共享库)
- 链接方式:动态库在程序运行时才被加载到内存。编译链接时,可执行文件仅记录对动态库中函数和数据的引用信息,而非实际代码。程序运行时,系统动态加载器负责将动态库映射到进程地址空间,供程序调用。
- 文件命名:同样以
lib
为前缀,但以.so
(Shared Object)作为后缀,如libexample.so
。
二 . 🌙 静态库的制作和使用
✨静态库的制作
编写Makefile构建静态库并使用
# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -g
# 定义源文件和目标文件
SRCS_LIB = add.c sub.c
OBJS_LIB = $(SRCS_LIB:.c=.o)
LIB_NAME = libmath.a
SRCS_MAIN = main.c
OBJS_MAIN = $(SRCS_MAIN:.c=.o)
EXEC = main
# 默认目标
all: $(LIB_NAME) $(EXEC)
# 生成静态库
$(LIB_NAME): $(OBJS_LIB)
ar rcs $@ $^
# 生成可执行文件
$(EXEC): $(OBJS_MAIN) $(LIB_NAME)
$(CC) $(CFLAGS) -o $@ $^ -L. -lmath
# 编译源文件为目标文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理生成的文件
clean:
rm -f $(OBJS_LIB) $(OBJS_MAIN) $(LIB_NAME) $(EXEC)
在Linux系统下制作静态库通常需要以下几个步骤:
1. ✨ 编写源文件
首先编写实现具体功能的C语言源文件。例如,创建两个源文件 add.c
和 sub.c
,分别实现加法和减法功能:
// add.c
int add(int a, int b) {
return a + b;
}
// sub.c
int sub(int a, int b) {
return a - b;
}
2. ✨编译生成目标文件
使用 gcc
编译器的 -c
选项将源文件编译为目标文件(.o
文件)。目标文件包含编译后的机器代码,但还不能直接执行,因为它可能还未解决外部符号引用等问题。在包含 add.c
和 sub.c
的目录下执行以下命令:
gcc -c add.c sub.c
这会生成 add.o
和 sub.o
两个目标文件。
3. ✨创建静态库
使用 ar
工具(GNU归档工具)将目标文件打包成静态库。静态库文件命名通常以 lib
前缀开头,以 .a
作为文件扩展名。执行以下命令创建名为 libmath.a
的静态库:
ar rcs libmath.a add.o sub.o
ar
命令的选项含义如下:
r
:将文件插入到归档文件中,如果文件已存在则替换它。c
:创建一个新的归档文件,如果归档文件已存在,不会报错。s
:为静态库创建索引,以加快链接速度。
🔑 静态库的使用
🔑 1. 编写调用静态库的源文件,和提供声明的头文件
制作math.h
头文件,它包含 add
和 sub
函数的声明。
// math.h
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
int sub(int a, int b);
#endif
使用以上制作出的静态库 libmath.a
,它包含 add
和 sub
函数的实现。现在编写一个 main.c
文件来调用这些函数:
#include <stdio.h>
#include "math.h"
// 声明要调用的函数
int add(int a, int b);
int sub(int a, int b);
int main() {
int result1 = add(3, 2);
int result2 = sub(3, 2);
printf("add result: %d\n", result1);
printf("sub result: %d\n", result2);
return 0;
}
🔑 2. 编译链接静态库
使用 gcc
编译器将 main.c
与静态库链接生成可执行文件。假设静态库 libmath.a
位于当前目录,使用以下命令:
gcc -o main main.c -L. -lmath
-o main
:指定生成的可执行文件名为main
。main.c
:要编译的源文件。-L.
:-L
选项用于指定库文件的搜索路径,.
表示当前目录。如果静态库位于其他目录,需将其路径替换.
。-lmath
:-l
选项用于指定要链接的库名,这里去掉了libmath.a
的lib
前缀和.a
后缀。
🔑 3. 运行可执行文件
链接成功后,在命令行执行生成的可执行文件:
./main
将会输出:
add result: 5
sub result: 1
三 🎈动态库的创建和使用
🚀动态库的制作
编写Makefile构建动态库并使用
# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -g -fPIC # -fPIC用于生成位置无关代码,是创建动态库必需的
LDFLAGS = -shared # 用于链接生成动态库
# 定义源文件和目标文件
SRCS_LIB = add.c sub.c
OBJS_LIB = $(SRCS_LIB:.c=.o)
LIB_NAME = libmath.so
SRCS_MAIN = main.c
OBJS_MAIN = $(SRCS_MAIN:.c=.o)
EXEC = main
# 默认目标
all: $(LIB_NAME) $(EXEC)
# 生成动态库
$(LIB_NAME): $(OBJS_LIB)
$(CC) $(LDFLAGS) -o $@ $^
# 生成可执行文件
$(EXEC): $(OBJS_MAIN)
$(CC) $(CFLAGS) -o $@ $^ -L. -lmath
# 编译源文件为目标文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理生成的文件
clean:
rm -f $(OBJS_LIB) $(OBJS_MAIN) $(LIB_NAME) $(EXEC)
在Linux系统中,动态库(也称为共享库)在程序运行时才被加载到内存,多个程序可以共享同一份动态库代码,从而节省内存。以下详细介绍动态库的创建和使用步骤。
🚀1. 编写源文件
假设我们要创建一个包含加法和减法函数的动态库,编写 add.c
和 sub.c
两个源文件:
// add.c
int add(int a, int b) {
return a + b;
}
// sub.c
int sub(int a, int b) {
return a - b;
}
🚀2. 生成目标文件
使用 gcc
编译器的 -fPIC
选项编译源文件,生成位置无关代码(Position Independent Code)的目标文件。-fPIC
选项确保生成的代码可以被加载到内存的任意位置,适用于动态库的特性。执行以下命令:
gcc -fPIC -c add.c sub.c
这将生成 add.o
和 sub.o
两个目标文件。
🚀3. 创建动态库
使用 gcc
编译器的 -shared
选项将目标文件链接成动态库。动态库文件通常以 lib
为前缀,以 .so
为扩展名。执行以下命令创建名为 libmath.so
的动态库:
gcc -shared -o libmath.so add.o sub.o
🔱 动态库的使用
🔱 1. 编写调用动态库的源文件,和提供声明的头文件
制作math.h
头文件,它包含 add
和 sub
函数的声明。
// math.h
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
int sub(int a, int b);
#endif
使用以上制作出的动态库 libmath.so
,它包含 add
和 sub
函数的实现。现在编写一个 main.c
文件来调用这些函数:
#include <stdio.h>
#include "math.h"
// 声明要调用的函数
int add(int a, int b);
int sub(int a, int b);
int main() {
int result1 = add(3, 2);
int result2 = sub(3, 2);
printf("add result: %d\n", result1);
printf("sub result: %d\n", result2);
return 0;
}
🔱 2. 编译链接动态库
使用 gcc
编译器将 main.c
与动态库链接生成可执行文件。假设动态库 libmath.so
位于当前目录,执行以下命令:
gcc -o main main.c -L. -lmath
其中,-L.
表示在当前目录查找库文件,-lmath
表示链接名为 libmath.so
的库(去掉 lib
前缀和 .so
后缀)。
🔱 3. 运行可执行文件
执行main.c时发现找不到共享库。
ldd main //显示可执行文件依赖的动态链接库
原因:
执行main文件时未告诉加载器动态库在哪 ,上述生成可执行文件main时只告诉了编译器动态库的位置.
由于动态库在运行时才被加载,系统需要知道动态库的位置。如果动态库不在系统默认的搜索路径中,可以通过以下几种方式解决:
- 直接安装到系统lib64目录或者/lib/x86_64-linux-gnu/下
- 在lib64目录或者/lib/x86_64-linux-gnu/下建立软连接指向动态库
- 导入环境变量将自己的库所在路径,添加到系统环境变量LD_LIBRARY_PATH中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/shiyue/liba
- 在/etc/ld.so.conf.d 建立一个以.conf结尾的文件,将库所在目录添入文件中去 让后执行ldconfig即可
使用以上第三种方案
成功执行!!!