简化版本
静态库:
- 制作:
gcc -g -c ./*.c -I ../include/
ar crs libmod.a *.o
- 使用
gcc -g main.c -L ./ -lmod -I ../include/
动态库:
- 制作:
gcc -g -c -fPIC -Wall ./*.c -I ../include/
gcc -g -fPIC -shared -o libmod.so ./*.o
or
gcc -g -fPIC -Wall ./*.c -I ../include/ -shared -o libmod.so
- 使用
gcc -g main.c -L ./ -lmod -I ../include/
注意,要将库放入可以被系统搜索到的位置,详情见下面的动态库使用
测试代码
下面是测试使用的代码
目录结构
├── include ## 库头文件
│ └── mod.h
├── source ## 库文件
│ ├── add.c
│ ├── div.c
│ ├── mul.c
│ └── sub.c
└── test ## 测试代码
└── main.c
库 头文件
库是一个非常简单的例子 就是加减乘除,但是这里我们重点是制作库 而不是里面的函数。
mod.h
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
int div(int a,int b);
add.c
#include "mod.h"
int add(int a,int b){
return a+b;
}
sub.c
#include "mod.h"
int sub(int a,int b){
return a-b;
}
mul.c
#include "mod.h"
int mul(int a,int b){
return a*b;
}
div.c
#include "mod.h"
int div(int a,int b){
return a/b;
}
test.c
#include "mod.h"
#include <stdio.h>
int main(){
printf("hello world\n");
printf("a + b = %d\n",add(1,3));
printf("a - b = %d\n",sub(1,3));
printf("a * b = %d\n",mul(1,3));
printf("a / b = %d\n",div(4,3));
return 0;
}
静态库
静态库简介
静态库被称为归档文件,是UNIX系统提供的第一种库。
实际上就是简单的一个普通的目标文件的集合,一般来说习惯用“.a”作为文件的后缀。
可以用ar这个程序来产生静态函数库文件。Ar是archiver的缩写。
- 可以将一组进程被用到的目标文件整合为单独的库文件,这样就可以使用它构建程序时,无需重新编译原来的源代码。
- 链接命令变得更为简单,只需要指定静态库名称即可。链接器知道如何搜索静态库并将可执行程序需要的对象抽取出来。
静态库函数允许程序员把程序link起来而不用重新编译代码,节省了重新编译代码的时间。不过,在今天这么快速的计算机面前,一般的程序的重新编译也花费不了多少时间,所以这个优势已经不是像它以前那么明显了。静态函数库对开发者来说还是很有用的,例如你想把自己提供的函数给别人使用,但是又想对函数的源代码进行保密,你就可以给别人提供一个静态函数库文件。理论上说,使用ELF格式的静态库函数生成的代码可以比使用共享函数库(或者动态函数库)的程序运行速度上快一些,大概1-5%
静态库优劣
- 优点:
- 隐藏代码内部细节,不需要暴露源码
- 无需重复编译静态库源码
- 无需外部链接,静态库会被编译进可执行程序内部,编译程序完成后,静态库可以删除
- 缺点:
- 静态库编译进可执行程序内部,可执行程序占用更多的磁盘空间
- 运行后的可执行程序,相同的静态库在代码段各自有一份,造成内存空间浪费
- 升级静态库,需要重新编译所有的可执行程序,无法模块化升级。
静态库命名规则
libname.a
- lib: 固定名称
- name: 库名
- .a :固定后缀
静态库制作
静态库的制作十分容易(相比动态库),仅需要以下指令。
- 将源码程序编译为.o文件
cd到source目录下,执行以下语句
gcc -g -c ./*.c -I ../include/
- -g :添加调试信息
- -c :编译为.o文件
- -I : 指定头文件目录
- 将.o文件打包为静态库
ar crs libmod.a *.o
- ar : 静态库打包命令
- crs:可选参数
- c 不在必须创建库的时候给出警告
- r 替换归档文件中已有的文件或加入新文件
- s 创建归档索引 (cf. ranlib)
后面跟库名称,以及.o文件
静态库使用
静态库的使用也很简单,只需要编译的时候注意一下链接就好。
cd 到test目录下
gcc -g main.c -L ../static/ -lmod -I ../include/
- L 指定库目录
- l(小L) 指定库名
- I(大i) 指定头文件目录
静态库注意
关于静态库需不需要头文件的问题?
不一定需要。创建一个库一般处于以下两种目的:
-
把一些相关的代码,打包成一个库,发布给其它的人用。
这中情况是最常见的情况,如写 C 语言用到 libgcc。在这种情况下,你除了提供库文件:静态库[ windows 下 .lib,linux .a];动态库:[Windows 下 .dll,Linux 下 .so] 之外,必须提供头文件。头文件是你这个库里面提供了那些接口可以供外界使用。 如果没有头文件,其他人无法使用,因为不知道函数方法的原型! -
在为某些软件项目写插件,而这些项目软件是公司内部的;或者说自己相对熟悉可接触的,即然是可以直接得知可能用到的函数方法的原型(函数名,参数列表,返回值等)的;就没有必要单独列出头文件,直接作为库使用也是可以的;很多大的项目,都是模块化设计,留有一些特定的接口,方便定制。当程序运行时,会动态加载指定目录下的动态库,运行时调用动态库里面约定好的方法。这种情况无需提供头文件,但要按照特定的约定来实现这个库。
总之:
当调用方还不知道不清楚函数原型的时候:动态库中的函数方法的原型(函数名,参数,返回值等)的情况下;
- 代码编写时候
调用方是不知道如何使用该库的,所以是需要头文件帮助,来编写调用代码的;尤其是用到了头文件中声明的类;类型和相关变量,相关函数;
然后库文件存在就可以直接调用.
- 代码编译时
如果是静态调用;无论是静态库或动态库,都是需要库的头文件参加编译的;
如果是动态加载动态库(dlopen/load等方法),则不需要头文件,只需要库文件.前提是调用方知道函数名和参数列表,返回值等信息,方可正确调用;
- 代码运行时
运行时,无论静态库还是 动态库,都不需要头文件;
参考链接:https://blog.csdn.net/weixin_42073232/article/details/110531589
静态库相关命令
ar命令详情见man手册和help
- 查看库内模块
liuwh@liuwh-PC ~/D/l/source> ar tv libmod.a
- 替换库内模块
liuwh@liuwh-PC ~/D/l/source> ar r libmod.a add.o
- 删除库内模块
liuwh@liuwh-PC ~/D/l/source> ar d libmod.a mul.o
- 追加库内模块
liuwh@liuwh-PC ~/D/l/source> ar q libmod.a mul.o
动态库
动态库简介
简单说,动态库是为了解决静态库的痛点出现的。
动态库的关键思想是,动态库被所有需要这些模块的程序共享,动态库不会像静态库一样被复制到链接的可执行程序中,相反,当第一个需要动态库中的模块的程序启动时(通常第一次加载动态库的程序会慢一点,后面的程序启动时不需要加载会快一些),库的副本才会被加载到内存中。
当后续进程需要该动态库中的模块时,可以直接使用已经加载到内存中的库的副本(放心,不会造成内存冲突,对于库内的变量都有各自的地址)。这样的好处是可执行程序需要的磁盘空间和虚拟内存空间减少了。
ps:为什么我会说模块呢?因为假如一个动态库有三个.o文件组成,那么只有在调用其中某个.o文件时才回去找该文件内的符号地址,不会一次性将三个模块都加载进来。(这是根据资料推测的,有待证实
标签:编译,静态,liuwh,int,linux,libmod,动态 From: https://www.cnblogs.com/zzLiuwh/p/17074760.html