文件名、后缀名
- 静态链接库文件的后缀名:Linux通常用
.a
,Windows通常用.lib
- 动态链接库文件的后缀名:Linux通常用
.so
,Windows通常用.dll
库类型 | Linux-GCC | Windows-MinGW | Windows-MSVC |
---|---|---|---|
静态库 | libxxx.a | libxxx.a | xxx.lib |
动态库 | libxxx.so | libxxx.dll | xxx.dll |
动态库附加文件 | libxxx.dll.a | xxx.lib |
- 动态库附加文件
libxxx.dll.a
有时会有其他形式:libxxx.a
、libxxxdll.a
,后者改变了库名
创建动态库
格式
gcc -fPIC -shared 源文件名(列表)... -o 动态链接库名
gcc -fPIC -shared 目标文件名(列表)... -o 动态链接库名
单个源文件/目标直接生成动态库
- 方法1:
gcc -fPIC -shared xxx.c -o libxxx.so
- 方法2:
gcc -fPIC -shared xxx.o -o libxxx.so
多个源文件/目标生成动态库
- 方法1:
gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so
或:gcc -fPIC -shared *.c -o libxxx.so
- 方法2:
gcc -fPIC -shared xxx1.o xxx2.o xxx3.o -o libxxx.so
备注
libxxx.so
可在Windows上改为libxxx.dll
Windows上小熊猫C++创建的C语言动态库TestShared-C
-
gcc.exe -c dllmain.c -o dllmain.o -O2 -pipe -DBUILDING_DLL=1
-
windres.exe -i TestShared-C_private.rc --input-format=rc -o Test-O coff
-
gcc.exe -mdll dllmain.o TestShared-C_private.res -o libTestShared-C.dll -s -static -Wl,--output-def,libTestShared-C.def,--out-implib,libTestShared-C.a
-
注:
-DBUILDING_DLL=1
是为了让dll.h
中的宏BUILDING_DLL
生效,下同
Windows上小熊猫C++创建的C++语言动态库TestShared-CPP
-
g++.exe -c dllmain.cpp -o dllmain.o -O2 -std=c++2a -pipe -DBUILDING_DLL=1
-
windres.exe -i TestShared-CPP_private.rc --input-format=rc -o TestShared-CPP_private.res -O coff
-
g++.exe -mdll dllmain.o TestShared-CPP_private.res -o libTestShared-CPP.dll -s -static -Wl,--output-def,libTestShared-CPP.def,--out-implib,libTestShared-CPP.a
总结
- 单步:
g++.exe -fPIC -mdll xxx1.c xxx2.c xxx3.c -o libxxx.dll -O2 -std=c++20 -pipe -s -static -Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a -DXXXXXX=1
解析:g++.exe -fPIC -mdll xxx1.c xxx2.c xxx3.c -o libxxx.dll
-O2 -std=c++20 -pipe -s -static
-Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a
-DXXXXXX=1
- 分步:
g++.exe -c xxx1.c xxx2.c xxx3.c -O2 -std=c++20 -pipe -DXXXXXX=1
或g++.exe -c xxx1.c -O2 -o xxx1.o -std=c++20 -pipe -DXXXXXX=1
g++.exe -fPIC -mdll xxx1.o xxx2.o xxx3.o -o libxxx.dll -s -static -Wl,--output-def,libxxx.def,--out-implib,libxxx.dll.a
gcc编译选项说明
-c
:只编译,不链接,生成目标文件.o
。-shared
:用于生成动态链接库(shared library)-mdll
:为DLL生成代码(Generate code for a DLL)-fPIC
:生成位置无关代码,方便动态链接,常用于编译动态链接库(shared library)-Wl,<选项>
:将逗号分隔的 <选项> 传递给链接器。--output-def <file>
:产生与导出符号相关的def文件(Generate a .DEF file for the built DLL)--out-implib FILE
:产生用于提供__img_符号的导入库(Generate import library)--kill-at
:从导出的符号中删除@nn(Remove @nn from exported symb)-Wl,--output-def,libTestShared-CPP.def,--out-implib,libTestShared-CPP.a
中不要有空格
创建静态库
格式
简单地说,静态库是一个目标文件的简单集合。因此,首先有目标文件。再把生成的目标文件编译为静态库。
- 第一步:先生成.o目标文件
gcc -c 源文件列表
gcc -c *.c
如gcc -c myfunc.c myproc.c
将得到myfunc.o
和myproc.o
。 - 第二步:由
ar
(archive,归档的意思)把多个目标文件集合起来编译为静态库。
ar rcs 静态链接库名称 目标文件1 目标文件2 ...
ar rcs libname.a *.o
或gcc *.o -static -o libname.a
如ar rcs libmyjob.a myfunc.o myproc.o
单个源文件/目标直接生成静态库
- 正确方法:
ar rcs libxxx.a xxx.o
- 错误方法:
ar rcs libxxx.a xxx.c
(静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)
多个源文件/目标生成静态库
- 正确方法:
ar rcs libxxx.a xxx1.o xxx2.o xxx3.o
- 错误方法:
ar rcs libxxx.a xxx1.c xxx2.c xxx3.c
(静态库可以生成;当运行连接了该静态库的可执行程序会报错:could not read symbols:Archive has no index;run ranlib to add one)
Windows上小熊猫C++创建的C语言静态库TestStatic-C
gcc.exe -c hello.c -o hello.o -O2 -pipe
windres.exe -i TestStatic-C_private.rc --input-format=rc -o TestStatic-C_private.res -O coff
ar r libTestStatic-C.a hello.o TestStatic-C_private.res
消息ar: creating libTestStatic-C.a
ranlib libTestStatic-C.a
Windows上小熊猫C++创建的C语言静态库TestStatic-CPP
g++.exe -c hello.cpp -o hello.o -O2 -std=c++2a -pipe
windres.exe -i TestStatic-CPP_private.rc --input-format=rc -o TestStatic-CPP_private.res -O coff
ar r libTestStatic-CPP.a hello.o TestStatic-CPP_private.res
消息ar: creating libTestStatic-CPP.a
ranlib libTestStatic-CPP.a
总结
g++.exe -c xxx1.c xxx2.c xxx3.c -O2 -std=c++20 -pipe
或g++.exe -c xxx1.c -O2 -o xxx1.o -std=c++20 -pipe
ar rcs libxxx.a xxx1.o xxx2.o xxx3.o
ar及其参数说明
- ar 是 Linux 的一个备份压缩命令,它可以将多个文件打包成一个备份文件(也叫归档文件),也可以从备份文件中提取成员文件。ar 命令最常见的用法是将目标文件打包为静态链接库。
- 参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
- 参数c:创建一个库。不管库是否存在,都将创建。
- 参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)
- 有些场合
ar rcs
又用ar -rc
、ar -r
、ar r
替代使用
链接(使用)动态库和静态库
方法1:-I 头文件目录 -L 库文件目录 -l库名xxx(无前缀lib和后缀名)
g++ main.o -o main -I {INCLUDE_PATH} -L {LIB_PATH} -lname
g++ main.cpp -o main -I {INCLUDE_PATH} -L {LIB_PATH} -lname
- 举例链接动态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -lxxx
- 举例链接静态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -lxxx
方法2:-I 头文件目录 -L 库文件目录 -l:完整文件名+后缀
g++ main.o -o main -I {INCLUDE_PATH} -L {LIB_PATH} -l:完整文件名+后缀
g++ main.cpp -o main -I {INCLUDE_PATH} -L {LIB_PATH} -l:完整文件名+后缀
- 举例链接动态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -l:libxxx.dll
- 举例链接静态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include -L D:\C++Lib\XXX\lib -l:libxxx.a
方法3:-I 头文件目录 文件路径+完整文件名+后缀
g++ main.o -o main -I {INCLUDE_PATH} {LIB_PATH_AND_LIBNAME}
g++ main.cpp -o main -I {INCLUDE_PATH} {LIB_PATH_AND_LIBNAME}
- 举例链接动态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -I D:\C++Lib\XXX\include D:\C++Lib\XXX\lib\libxxx.dll
- 举例链接静态库:
g++ main.cpp -o main.exe -O2 -std=c++20 -pipe -s -static -I D:\C++Lib\XXX\include D:\C++Lib\XXX\lib\libxxx.a
说明:
-I 头文件目录
可用设置环境变量set C_INCLUDE_PATH=头文件目录
替代-L 库文件目录
可用设置环境变量set LIBRARY_PATH=库文件目录
替代文件路径+完整文件名+后缀
不能省略路径,但可以用相对路径,LIBRARY_PATH
对方法3无效
其他
名称修饰
- 使用
extern "C"
来关闭名称修饰 - 使用编译器选项关闭名称修饰:一些编译器提供了选项来关闭名称修饰,例如在GCC编译器中,可以使用
-fno-rtti
选项关闭名称修饰。
函数导出
- MSVC
def文件
、__declspec(dllexport)
- MinGW64
__declspec(dllexport)
- cmake+MinGW64 默认会导出所有函数
环境变量
C_INCLUDE_PATH
头文件路径的环境变量:CPLUS_INCLUDE_PATH
头文件路径的环境变量:LIBRARY_PATH
库文件路径的环境变量LD_LIBRARY_PATH
是一个Linux环境变量,用于指定动态链接库的搜索路径。当一个程序运行时,如果需要使用动态链接库,系统会在LD_LIBRARY_PATH
中指定的路径中搜索相应的库文件,以便程序能够正确运行。
查看用命令
nm libxxx.a
查看库中包含的函数信息ldd main
查看可执行程序依赖的动态库
fpic和fPIC比较
- 相同点:都是为了在动态库中生成位置无关的代码,通过全局偏移表(GOT)访问所有常量地址。程序启动时动态加载程序解析GOT条目。
- 不同点:如果链接的可执行文件的GOT大小超过计算机架构特定的最大值,则会在编译链接时报错误消息,提示 -fpic 不起作用;因此在这种情况下,需要使用 -fPIC 重新编译。GOT大小因芯片架构的不同而大小不一样,SPARC上为8k,在AArch64上为28k(笔者遇到的就是它,GOT超出了限制,所以报错),在m68k和RS / 6000上为32k。而x86上没有此限制。
- 温馨提示:为了保障程序在跨平台编译时整体可用,通常情况下建议都使用 fPIC