1.如果要将多个.o文件生成一个库文件,则存在两种类型的库,在linux里面后缀是.a,另一种是动态库,后缀为.so。
当可执行程序要与静态库进行链接时,所用到的库中的函数和数据会被拷贝到最终的可执行程序中。而采用动态库则不会,程序加载器在后台为我们讲所需的动态库自动加载到内存中且保证整个系统只有一份拷贝。
如果一个系统中存在多个需要同时运行的程序且这些程序之间存在共享库,那么采用动态库的形式将更节省内存。但是对于嵌入式系统,大多数情况下都是整个软件就是一个可执行程序且不支持动态加载的方式,即以静态库为主。
eg:
创建foo.c bar.c文件,
1 #include <stdio.h> 2 void bar() 3 { 4 printf("This is bar()!\n"); 5 }
1 #include<stdio.h> 2 void foo() 3 { 4 printf("This is foo()!\n"); 5 }
然后把这两个函数放在静态库libmy.a中,在次之前,需要将他们编译成.o目标文件,然后生成静态库。
gcc -c foo.c bar.c
ar crs libmy.a foo.o bar.o
ar的c参数表示创建一个档案文件,而r参数指示将文件增加到所创建的库文件中,s参数就是为了生成库索引以提高库被链接时的效率。
ar --help:
用法:ar [仿真选项] [-]{dmpqrstx}[abcDfilMNoPsSTuvV] [--plugin <名称>] [成员名] [计数] 归档 文件…
ar -M [<mri-脚本]
命令:
d - 从归档文件中删除文件
m[ab] - 在归档文件中移动文件
p - 打印在归档文件中找到的文件
q[f] - 将文件快速追加到归档文件中
r[ab][f][u] - 替换归档文件中已有的文件或加入新文件
s - 作为 ranlib 工作
t - 显示归档文件的内容
x[o] - 从归档文件中分解文件
特定命令修饰符:
[a] - 将文件置于 [成员名] 之后
[b] - 将文件置于 [成员名] 之前 (于 [i] 相同)
[D] - 将 0 用于时间戳和 uid/gid(默认)
[D] - 使用实际时间戳和 uid/gid
[N] - 使用名称的实例 [数量]
[f] - 截去插入的文件名称
[P] - 在匹配时使用完整的路径名
[o] - 保留原来的日期
[u] - 只替换比当前归档内容更新的文件
通用修饰符:
[c] - 不在必须创建库的时候给出警告
[s] - 创建归档索引 (cf. ranlib)
[S] - 不要创建符号表
[T] - 产生一个简单归档
[v] - 输出较多信息
[V] - 显示版本号
@<file> - 从 <file> 读取选项
--target=BFDNAME - 指定目标对象格式为 BFDNAME
可选项:
--plugin <p> - 加载指定的插件程序
仿真选项:
没有仿真特有的选项
库一旦生成,我们可以检测其可用性,main.c如下:
1 extern void foo(); 2 extern void bar(); 3 int main (void) 4 { 5 foo(); 6 bar(); 7 return 0; 8 }
执行:gcc main.c libmy.a -o app
./app
可以看到函数输出。
ar t libmy.a 可以看到libmy库的内容,(t参数,详情man)
d参数可以删除库中的目标文件:
ar d libmy.a foo.o
2.或许不如你所期
将上面的生成可执行文件命令做点更改:gcc main.c libmy.a -o app改成gcc libmy.a main.c -o app
居然报错了,找不到依赖项了:
这样可以看出,gcc对于依赖项需要从左往右指定。gcc的这种奇怪特性造成当依赖关系比较复杂时需要对同一个库在不同的位置指定多次,否则就会出现无法成功链接的情形。
3.需要知道的小东西
-L选项用于告诉gcc从哪个目录查找库文件,可以多次使用它指定多个目录;
-l(小L)选项用于告诉gcc在生成可执行程序是需要链接的库名,这一选项同样可以多次使用以指定多个库。
使用-l选项时要注意,后面所跟名字并不包括lib前缀和.a后缀,比如上面例子中的-lmy代表指定libmy.a参与链接。
测试发现,-L和-l选项有着依赖关系,可以只是用-L选项,后面使用库的全名,但是不能只使用-l选项,否则将出现erro。