初始目录如图,文件内容在本文末尾

1.静态库
1.静态库的概念和特点
1. 静态库的概念
静态库(.a文件)在编译时直接嵌入到可执行程序中,包含多个目标文件(.o)的集合。程序运行时无需外部依赖,但会增加可执行文件体积。
2. 静态库的特点
优点:
- 编译后与程序完全绑定(意味着使用简单)
- 无需运行时加载库文件(意味着速度更快)
缺点:
- 库更新需重新编译程序
- 相同库在多程序中重复占用内存
2.静态库的制作和使用(Linux)
1.生成目标文件
#gcc -c 源文件清单 -I 指定所需的自定义头文件目录
gcc -c add.c sub.c mult.c div.c -I ../include/
-c 表示仅编译,生成目标文件(.o文件)但不进行链接
执行后会生成:add.o sub.o mult.o div.o

2.打包生成静态库
ar rcs cala.a add.o sub.o mult.o div.o
rcs: r 替换旧成员 c 创建新库 s 添加索引

3.静态库的使用
gcc main.c cala.a -o app.exe -I ../include
./app
使用 -I 制定静态库的位置
此时可执行文件 app已包含所有库代码

可以将 app 移动到其他位置,依然可以正常运行

2.动态库
1.动态库的概念和特点
1.动态库的概念
动态库(.so文件)在程序运行时加载,可被多个程序共享。库更新后,程序无需重新编译。
2.动态库的特点
优点:
-
多个程序共享内存中的同一份库
- 库更新无需重新编译程序
缺点:
-
运行时需确保库路径正确(意味着使用麻烦,容易出错)
- 增加运行时加载开销(意味着速度稍慢)
2.动态库的制作和使用(Linux)
将整个工作区恢复到初始目录的样子
1.生成目标文件(位置无关的目标文件)
# 使用 -fPIC 编译选项制定生成位置无关目标文件
gcc -c add.c sub.c mult.c div.c -fPIC -I ../include/
静态库(.a)在链接时固定地址,无需位置无关性
动态库(.so)需在运行时加载到任意内存地址,必须位置无关
同样生成四个.o目标文件

2.创建动态库
gcc -shared -o libcala.so add.o sub.o mult.o div.o
3.动态库的使用
# 编译链接动态库
gcc main.c -o app.exe -L./ -lcala -I ../include
# 运行时指定库路径
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
./app
-L./ : 指定库搜索路径(为 ./ 即当前目录)
-lcala: 链接名为 libcala.so的库(无需给出前缀lib和后缀.so)

此时如果将app移动至其他位置,无法正确运行:

使用 ldd 查看 app 依赖的库:

可以发现确实找不到库文件
解决办法:
将当前目录加到环境变量 LD_LIBRARY_PATH 中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<动态库所在的目录>

使用 echo 查看是否成功添加

再运行 app,发现没有问题

但是在终端中修改 LD_LIBRARY_PATH 只有在这个终端中才生效,如果重新开一个终端又会运行不了,怎么解决?
前往用户目录,使用 vim 编辑 .bashrc 文件

vim编辑的方法本文不再赘述,网络上教程很多,到文件末尾然后添加:
将下面的 xxxxxxxxxxxxx/Part2/src 改为你自己的动态库的位置即可
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxxxxxxxxxxx/Part2/src
然后 ESC ,:wq 保存退出即可
之后只要动态库的位置 和 .bashrc 文件中关于动态库位置的设置不改变,无论 app 身在何处都可以正常运行。
3.补充
1.静态库动态库选择
静态库与动态库同时存在,编译器优先使用动态库。
静态库和动态库有各自的优缺点,根据实际情况选择较优的即可。
2.头文件与库文件区别
头文件仅仅只是.h 或 .hpp 文件,库文件是 .a 或 .so 文件,头文件没能成为库文件是因为头文件不如库文件通用,如果一个项目中某些模块被频繁使用,并且可以制作为库文件,则可以将其制作为库文件。还有一些代码提供商不愿意将源代码交给乙方,则可以将自己负责的代码制作为库文件打包给乙方,乙方只能使用,无法查看和修改源代码内容。
3.使用Makefile制作库文件
# 编译器设置
CC = gcc
CFLAGS = -Wall -g
INCLUDES = -I../include # 头文件目录
LIB_DIR = -L. # 库文件目录(当前目录)
# 库文件
STATIC_LIB = libcala.a # 使用标准静态库命名
DYNAMIC_LIB = libcala.so # 使用标准动态库命名
# 最终可执行程序
STATIC_APP = app_static
DYNAMIC_APP = app_dynamic
# 源文件和目标文件
SRCS = add.c sub.c mult.c div.c
OBJS = $(SRCS:.c=.o)
# 所有目标
all: $(STATIC_APP) $(DYNAMIC_APP)
# 静态库目标
$(STATIC_LIB): $(OBJS)
ar rcs $@ $^
# 动态库目标
$(DYNAMIC_LIB): $(OBJS)
$(CC) -shared -o $@ $^
# 静态可执行程序
$(STATIC_APP): main.o $(STATIC_LIB)
$(CC) main.o -o $@ $(LIB_DIR) -lcala
# 动态可执行程序
$(DYNAMIC_APP): main.o $(DYNAMIC_LIB)
$(CC) main.o -o $@ $(LIB_DIR) -lcala
# 目标文件规则
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -fPIC
main.o: main.c
$(CC) $(CFLAGS) $(INCLUDES) -c $<
# 清理
clean:
rm -f *.o *.a *.so $(STATIC_APP) $(DYNAMIC_APP)
# 测试目标
test: all
@echo "=== Testing static application ==="
./$(STATIC_APP)
@echo "\n=== Testing dynamic application ==="
LD_LIBRARY_PATH=. ./$(DYNAMIC_APP)
.PHONY: all clean test
Makefile 文件放在 src 目录下

执行 make

分别运行 app_static 和 app_dynamic

本文中使用的源文件如下:
// main.c
#include <stdio.h>
#include "calculator.h"
int main() {
int a = 30, b = 5;
printf("a=%d, b=%d\n", a, b);
printf("add: %d\n", add(a, b));
printf("sub: %d\n", sub(a, b));
printf("mult: %d\n", mult(a, b));
printf("div: %d\n", div(a, b));
return 0;
}
// calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
int add(int x, int y);
int sub(int x, int y);
int mult(int x, int y);
int div(int x, int y);
#endif
// add.c
#include "calculator.h"
int add(int x, int y) {
return x + y;
}
// sub.c
#include "calculator.h"
int sub(int x, int y) {
return x - y;
}
// mult.c
#include "calculator.h"
int mult(int x, int y) {
return x * y;
}
// div.c
#include "calculator.h"
int div(int x, int y) {
if(y == 0) return 0; // 避免除零错误
return x / y;
}

724

被折叠的 条评论
为什么被折叠?



