静态库
静态库的命名方式为libxxx.a
root@xxx:/home/gen_static_lib# ls
__init__.py libtest.a main main.c readme.md test.c test.o
root@xxx:/home/gen_static_lib# pwd
/home/gen_static_lib
root@tfs:/home/gen_static_lib# cat test.c
// 计算start到end之间所有整数的和
int sum(int start, int end){
int res = 0;
for (; start <= end; start++){
res += start;
}
return res;
}
/*
执行命令
gcc -c test.c -o test.o
ar rcs lbtest.a test.o
ls |grep test.
此时libtest.a就成功生成了,然后我们再来编写一个main.c来调用
*/
root@tfs:/home/gen_static_lib# cat main.c
#include<stdio.h>
int sum(int, int);
/*
这里只声明了sum,但是具体实现没编写,因为它已经在libtest.a里面实现了,我们只需要在gcc编译的时候指定即可
指定的方式为:
gcc main.c -L . -l test -o main
./main
5050
参数解释:
gcc main.c无需解释,表示对main.c文件进行编译。而结尾的-o main也无需解释,表示生成可执行文件的文件名叫main
中间的 -L .表示追加库文件(也就是说要追加下面-l test选项的路径)的搜索路径,因为gcc在寻找库的时候,只会从标准位置进行查找。因此需要通过-L 参数将写好的
静态库所在的路径追加进去,libtest.a位于当前目录,所以是 -L .
然后是 -l test (注: -l test 也可以写成 -ltest ,即中间没有空格,这种写法更为常见。但这里我为了清晰,之间加了
一个空格,对于编译结果是没有影响的。),首先-l表示要链接的静态库(也可以是动态库,后面会说,目前就只看静态库即可),因为当前的静态库
名字叫做libtest.a,那么把开头的lib和结尾的.a去掉再和-l进行组合即可
如果我们将静态库改名为 libxxx.a 的话,那么就需要指定 -l xxx ;同理,要是我们指定的是 -l foo ,那么在链
接的时候会自动寻找 libfoo.a。所以从这里也能看出,在 Linux 中创建静态库的时候一定要遵循命名规范,以 lib
开头、.a 结尾,否则链接是会失败的。当然追加搜索路径、链接静态库的数量是没有限制的,比如除了 libtest.a
之外还想链接 libfoo.a,那么就指定 -l test -l foo 即可。
同理还有头文件,虽然这里没有涉及到,但还是需要说一说,因为导入头文件更常。如果想导入的头文件不在搜
索路径中,我们在编译的时候也是需要指定的。假设 main.c 还引入了一个自定义的头文件,其位于当前目录下的
header 目录里,那么编译的时候为了让编译器能够找得到,我们需要通过 -I 来追加相应的头⽂件的搜索路径:
gcc main.c -I ./header -L . -l test -o main
-I: 追加头文件搜索路径
-L:追加库文件搜索路径
-l:链接指定的静态库或动态库
对于头文件搜索路径、库文件搜索路径、引入的静态库的数量,都是没有限制的,可以指定任意个:-I 、 -L 、 -l
*/
int main() {
printf("%d\n", sum(1, 100));
return 0;
}
动态库
动态库的命名方式为libxxx.so
// 还是test.c文件
root@xxx:/home/gen_dynamic_lib# cat test.c
// 计算start到end之间所有整数的和
int sum(int start, int end){
int res = 0;
for (; start <= end; start++){
res += start;
}
return res;
}
/*
执行命令
gcc test.c -shared -o libtest.so
在Linux中,动态库也具有相同的命名规范,只不过它是以.so结尾。但是你真的不按照这个格式命名也是可以id,只不过在使用gcc的
时候会找不到相应的库。因为编译的时候会按照指定格式去找库文件,所以我们在生成库文件的时候也要按照相同的格式起名字
Windows 上生成动态库的方式与 Linux 相同,只需把动态库的后缀 .so 换成 .dll 即可。
*/
root@tfs:/home/gen_dynamic_lib# cat main.c
#include<stdio.h>
int sum(int, int);
/*
这里只声明了sum,但是具体实现没编写,因为它已经在libtest.so里面实现了,我们只需要在gcc编译的时候指定即可
指定的方式为:
gcc main.c -L . -l test -o main1
我们看到可执行文件成功生成了,这里起名为 main1。引入动态库和引入静态库的方式是一样的,因为 -l 既可
以链接静态库、也可以链接动态库。
./main1
./main1: error while loading shared libraries: libtest.so:
cannot open shared object file: No such file or directory
但是问题来了,虽然编译成功了,但是执行的时候却报错了,说找不到这个 libtest.so,尽管它就在当前可执行
件所在的目录下。
原因是可执行件在查找动态库的时候也是会从指定的位置进⾏查找的,而我们当前目录不在搜索范围内。这时候
可能有人会好奇,我们不是在编译的时候通过 -L 参数将当前路径追加进去了吗?
答案是动态库和静态库不同,动态库在链接的时候自身不会被包含在可执行文件当中,我们指定的 -L . -l
test 相当于只是在链接的时候告诉即将生成的可执行文件:"在当前目录下有一个 libtest.so,它将来会是你的依
赖,你赶紧记录下"。我们可以通过 ldd 命令查看可执行文件依赖的动态库:
root@xxx:/home/gen_dynamic_lib# ldd main1
linux-vdso.so.1 (0x00007fffd5115000)
libtest.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f39b1f67000)
/lib64/ld-linux-x86-64.so.2 (0x00007f39b2169000)
我们看到 libtest.so 已经被记录下来了,所以链接动态库时只是记录了动态库的信息,当程序执行时再去动态加
载,因此它们会有一个指向。但我们发现 libtest.so 指向的是 not found,这是因为动态库 libtest.so 不在动态库
查找路径中,所以会指向 not found。
因此我们还需要将当前模录加载到动态库查找路径中,vim /etc/ld.so.conf,将当前目录( 我这里是 /home/gen_dynamic_lib )追加
在里面,或者直接 echo "/home/gen_dynamic_lib" >> /etc/ld.so.conf ,然后执行 /sbin/ldconfig 使得修改生效。
再次运行,可以发现成功了
*/
int main() {
printf("%d\n", sum(1, 100));
return 0;
}