静态、动态链接库的生成及使用

静态、动态库简介

  • 静态库:在链接过程中链接到可执行文件
    优点:可执行文件运行时不再依赖静态库
    缺点:每个使用该库的程序都包含相同的公共代码,浪费资源;
       使可执行文件体积相对较大;
  • 动态库:在程序运行过程中加载
    优点:只在内存中加载一次,可在不同程序间共享,节省内存;
       重新构建某些模块时只需替换相应的dll / so文件,无需重新编译整个程序
       按照函数调用约定,可以在不同语言间使用;(dll在该方面有一定局限性)
       基于以上,动态库有更好的可维护性和可扩展性
    缺点:程序运行时依赖动态库

静态库是牺牲空间效率,换取时间效率,共享库是牺牲时间效率换取空间效率


Linux下静态链接库的生成

实际上,.o文件即是用于链接的目标文件了,

gcc/clang -c xxx.c

得到目标文件xxx.o

多个.o文件可以使用 ar (archive) 命令打包为静态库.a文件,
其命名规范为libxxx.a,其中xxx为库的名字,.a archive

//将yyy.o添加至静态库libxxx.a中
ar -r libxxx.a yyy.o

//从libxxx.a中移除yyy.o
ar -d libxxx.a yyy.o

//查看libxxx.a归档的文件
ar -t libxxx.a
libxxx.a静态库链接方式
clang hello.o -L. -lnotfastjson -o hello.exe

clang -o hello.exe hello.c -L. libnotfastjson.a

gcc hello.c libnotfastjson.a -o hello.exe

其中,命令参数
-Lpathpath 表示链接库的搜索目录 -L. 表示当前目录
-lnamename 是链接库的名字 -lxxx 表示链接名为libxxx.a的库

Linux下动态链接库的生成及使用

.so文件,shared object

//将.c文件编译为.so文件
gcc --share -o libnotfastjson.so  access.c hash_table.c memory.c parse.c

//.so文件参与编译生成可执行文件
gcc hello.c libnotfastjson.so -o hello.exe

gcc 生成.so文件时可能需要命令参数 -fPIC,表明生成位置无关代码(Position-Independent Code),
生成的代码中全部使用相对地址,可以加载到内存的任意位置,使库真正被共享
否则每个使用该动态库的程序都要生成一份库代码的副本

gcc编译参数-fPIC的一些问题 http://blog.sina.com.cn/s/blog_54f82cc201011op1.html


Win下动态库dll及静态库lib文件的生成

Windows下动态链接库(dynamic link library)由lib文件与与dll文件组成
lib文件包含函数所在的dll信息和dll中函数位置信息(函数入口)
程序在运行时加载dll文件中包含的代码

静态链接库(static link library)指lib文件包含函数代码本身,编译时直接将库链接至可执行文件

使用VS建立对应工程项目,将要输出到dll中的函数声明用__declspec(dllexport)修饰

//xxx.h
__declspec(dllexport) int add(int a, int b);

//xxx.c
__declspec(dllexport) int add(int a, int b){
	return a + b;
}

编译输出结果为对应的dll文件、lib文件或静态库lib文件(注意lib文件导出的为所有函数,static函数 除外)
或使用命令行方式得到lib文件

//VC工具lib命令
lib a.obj b.obj /out:mylib.lib

若不使用__declspec(dllexport)定义导出函数,可以使用def文件声明导出的函数,否则导出空dll

在VS中添加新def文件会自动将文件添加至链接输入,自建文件需要手动在项目属性添加链接器输入

LIBRARY notfastjson
EXPORTS
nfjson_parse			@1
nfjson_get_type			@2
//函数名[@数字标号],数字标号在某些情况下的函数调用用到
···

def文件说明 https://blog.csdn.net/sinat_22991367/article/details/73724671

另:使用VS的 dumpbin 工具或 pexports 工具由dll文件导出def文件,
dumpbin 导出的def文件需要修改为以上格式

dumpbin xxx.dll /exports /out:xxx.def
pexports xxx.dll -o > xxx.def

以上两种方式均可声明导出至dll的函数

此外,gcc 也可将.c文件编译为dll,但是其实际格式可能与cl生成有区别,
某些情况下(如debug模式)可用,因此不建议用gcc编译dll文件

Win下动态库dll及静态库lib文件的使用

注意 win下的库需要区分32位、64位及库的产出方式(debug,realease),
如64位Realease输出的.dll 无法在32位debug下使用

示例文件目录 test.c、notfastjsonlib.h、(静态库)static-notfastjson.lib、(动态库)notfastjson.lib、notfastjson.dll

  • 静态库.lib的使用
    包含对应头文件,使用#pragma预处理命令声明包含静态库static-notfastjson.lib
//test.c
#include"notfastjsonlib.h"
#pragma comment(lib,"static-notfastjson.lib")
  • 动态库.dll的使用

隐式调用 需要.lib文件、.dll文件和.h文件
在VS项目属性的链接器选项中添加附加依赖项.lib,或者使用预编译命令

#include"notfastjsonlib.h"
#pragma comment(lib,"notfastjson.lib")

告知编译器使用该lib文件对应的dll
并且在代码中引入库的头文件,即可调用该dll

显式调用 只需要dll文件,但是要求知道库中的函数名
引入对应头文件与Windows.h
使用LoadLibrary系列函数动态加载dll文件
使用GetProcAddress函数获取函数入口
使用FreeLibrary卸载dll文件

#include"notfastjsonlib.h"
#include <Windows.h>
typedef int (*_func)(int arg);
int main() {
    HINSTANCE notfastjson = LoadLibrary(L"notfastjson.dll");//宽字符参数
    if (!notfastjson) { printf("load dll failed"); return -1; }
    _func func= GetProcAddress(notfastjson, "func");
    if (!func) { printf("find function failed"); return -1; }
    func(10);//使用dll中函数
    FreeLibrary(notfastjson);
	return 0;
}

此外,同一项目生成的静态库文件一般比静态库文件大很多,

dll和exe都是PE文件,.so文件是ELF文件,内容格式简要
PE(Portable Executable)文件是Windows下的可执行文件,如exe、dll等
ELF文件包括
  可重定位文件,Relocatable File,如.o
  可执行文件,Executable File,如bash,ls
  共享目标文件,Shared Object File,如.so
  核心转储文件,Core Dump File,当进程意外终止,系统可以将该进程地址空间的内容及终止时的一些信息转存到核心转储文件
PE与ELF文件格式都是基于Unix的COFF(Common Object File Format)文件格式

lib和a是静态库文件,都是.o文件的归档,包含众多编译信息


附:lib文件、dll文件、.a文件、def文件的编译及相互转换

MinGW:

c -> o          	gcc -c a.c
c -> exe        	gcc a.c libs.o -o a.exe (从主程序a.c,附加libs,生成a.exe)
o -> exe        	gcc a.o b.o ... -o main.exe
c -> dll,def,a  	gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a
a -> dll          	a2dll liba.a 
dll -> a:         	dlltool --dllname a.dll --def a.def --output-lib liba.a (需要def文件)
a -> def:       	dumpbin /exports lib.a > lib.def (在windows上调用,def需要修改)
dll -> def :     	pexports a.dll -o > a.def (这里的-o是指给函数标序号)
lib -> def :     	reimp -d a.lib
lib -> a:         	(for __cdecl functions in most case) reimp a.lib; (for __stdcall functions)

MSVC:

c -> lib    cl /LD a.c (注意已经定义了export列表)
c -> dll    cl /LD a.c
c -> obj    cl /c a.c
c -> exe    cl a.c /out:a.exe
dll ->lib   lib /machine:ix86 /def:a.def /out:a.lib (需要def文件)
obj ->lib   lib a.obj b.obj... /out:mylib.lib
dll ->def  DUMPBIN a.dll /EXPORTS /OUT:a.def (生成的def需要做修正)
lib ->def   reimp -d a.lib (MSYS+MinGW下)

关于dll、def、lib等在Win与Linux下的进一步讨论见

MSVC vs. MinGW 之dll玩转攻略手记【转】http://blog.sina.com.cn/s/blog_4f183d960100gqfj.html


2019/11/19

发布了85 篇原创文章 · 获赞 83 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/kafmws/article/details/103116142