a.c -> a.out
foo()
bar()
hum()
main()
单一模型:将程序中所有功能全部实现于一个单一的源文件内部。编译时间长,不易于维护和升级,不易于协作开发。
分离模型:将程序中的不同功能模块划分到不同的源文件中。缩短编译时间,易于维护和升级,易于协作开发。
a.c -> a.o \
foo() | -> ...
bar() |
b.c -> b.o /
hum()
a.o \
b.o | -> 库 + 其它模块 -> ...
c.o |
... /
1.静态库
静态库的本质就是将多个目标文件打包成一个文件
链接静态库就是将库中被调用的代码复制到调用模块中
使用静态库的程序通常会占用较大的空间,库中代码一旦修改,所有使用该库的程序必须重新链接
使用静态库的程序在运行时无需依赖库,其执行效率高
静态库的形式:libxxx.a
构建静态库:
.c -> .o -> .a
ar -r libxxx.a x.o y.o z.o
^ \______/
|_______|
使用静态库:
gcc ... -lxxx -L<库路径>
export LIBRARY_PATH=<库路径>
gcc ... -lxxx
2.动态(共享)库
动态库和静态库最大的不同就是,链接动态库并不需要将库中被调用的代码复制到调用模块中,相反被嵌入到调用模块中的仅仅是被调用代码在动态库中的相对地址。
如果动态库中的代码同时为多个进程所用,动态库的实例在整个内存空间中仅需一份,因此动态库也叫共享库或共享对象(Shared Object, so)。
使用动态库的模块所占空间较小,即使修改了库中的代码,只要接口保持不变,无需重新链接。
使用动态库的代码在运行时需要依赖库,执行效率略低。
动态库的形式:libxxx.so 使用ldd命令可以查看可执行程序依赖的动态库
构建动态库:
gcc -c -fpic xxx.c -> xxx.o
|
生成位置无关码
库内部的函数调用也用相对地址表示
gcc -shared -o libxxx.so x.o y.o z.o
^ \______/
|______|
使用动态库:
gcc ... -lxxx -L<库路径>
export LIBRARY_PATH=<库路径>
gcc ... -lxxx
运行时所调用的动态库必须位于LD_LIBRARY_PATH环境变量所表示的路径中。
gcc缺省链接共享库,可通过-static选项强制链接静态库。
3.动态加载动态库
#include <dlfcn.h> \ 系统提供的针对动态
-ldl / 库的动态加载函数集
void* dlopen(const char* filename, int flag);
成功返回动态库的句柄,失败返回NULL。
filename - 动态库路径,若只给文件名,则根据LD_LIBRARY_PATH环境变量搜索动态库
flag - 加载方式,可取以下值:
RTLD_LAZY - 延迟加载,使用动态中的符号时才加载
RTLD_NOW - 立即加载
该函数所返回的动态库句柄唯一地标识了系统内核所维护的动态库对象,将作为后续函数调用的参数
void* dlsym(void* handle, const char* symbol);
成功返回函数地址,失败返回NULL
handle - 动态库句柄
symbol - 符号(函数或全局变量)名
该函数所返回的函数指针是void*类型,需要强制类型转换为实际的函数指针类型才能调用
int dlclose(void* handle);
成功返回0,失败返回非零
handle - 动态库句柄
char* dlerror(void);
之前若有错误发生则返回错误信息字符串,否则返回NULL
#include <stdio.h>
#include <dlfcn.h>
int main(void) {
// 动态加载动态库libmath.so
void* handle = dlopen("shared/libmath.so", RTLD_NOW);
if (! handle) {
fprintf(stderr, "dlopen: %s\n", dlerror());
return -1;
}
// 从动态库中获取add函数的入口地址
int (*add)(int, int) = (int (*)(int, int))dlsym(handle, "add");
if (! add) {
fprintf(stderr, "dlsym: %s\n", dlerror());
return -1;
}
// 从动态库中获取sub函数的入口地址
int (*sub)(int, int) = (int (*)(int, int))dlsym(handle, "sub");
if (! sub) {
fprintf(stderr, "dlsym: %s\n", dlerror());
return -1;
}
// 从动态库中获取show函数的入口地址
void (*show)(int, char, int, int) = (void (*)(int, char, int, int))dlsym(handle, "show");
if (! show) {
fprintf(stderr, "dlsym: %s\n", dlerror());
return -1;
}
int a = 30, b = 20;
show(a, '+', b, add(a, b));
show(a, '-', b, sub(a, b));
if (dlclose(handle)) {
fprintf(stderr, "dlclose: %s\n", dlerror());
return -1;
}
return 0;
}