# 静态库
对源文件进行汇编操作 (使用参数-c)
得到二进制的目标文件 (.o 格式)
, 然后通过 ar工具
将目标文件打包就可以得到 静态库文件``(libxxxx.a)
# ar
参数
- 参数 c: 创建一个库,不管库是否存在
- 参数 s: 创建目标文件索引,在创建较大库时加快时间
- 参数 r: 在库中插入模块 (替代)。默认新的成员添加在库的结尾处,若该模块已经存在,则替换同名模块
# 生成静态链接库
| ar rcs 静态库名字(libxxx.a) 原材料(*.0) |
| |
| 1,提供头文件**.h |
| 2,提供制作出来的静态库 libxxxx.a |
# 静态库测试
通过对函数生成对应的静态库 .a
文件,然后根据其头文件以及对应的 静态库文件
,即可让其它函数进行调用
| #include <stdio.h> |
| #include "head.h" |
| |
| int add(int a, int b) |
| { |
| return a+b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| int subtract(int a, int b) |
| { |
| return a-b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| int multiply(int a, int b) |
| { |
| return a*b; |
| } |
| #include <stdio.h> |
| #include "head.h" |
| |
| double divide(int a, int b) |
| { |
| return (double)a/b; |
| } |
| #ifndef _HEAD_H |
| #define _HEAD_H |
| |
| int add(int a, int b); |
| |
| int subtract(int a, int b); |
| |
| int multiply(int a, int b); |
| |
| double divide(int a, int b); |
| #endif |
| #include <stdio.h> |
| #include "head.h" |
| |
| int main() |
| { |
| int a = 20; |
| int b = 12; |
| printf("a = %d, b = %d\n", a, b); |
| printf("a + b = %d\n", add(a, b)); |
| printf("a - b = %d\n", subtract(a, b)); |
| printf("a * b = %d\n", multiply(a, b)); |
| printf("a / b = %f\n", divide(a, b)); |
| return 0; |
| } |
# 生成静态库
| |
| $ gcc add.c div.c mult.c sub.c -c |
| |
| ar rcs lib名称.a add.o div.o mult.o sub.o |
| //即可生成静态库 |
# 动态库
动态库时 运行时
加载的库,是多个程序可以共用的 共享库
。动态链接库是目标文件的集合,使用相对地址,其真实地址是在应用程序加载动态库时形成的。 Linux
以 .so
作为后缀。 libxxx.so
. window
使用以 lib
作为前缀,以 dll
作为后缀, libxxx.dll
.
动态链接库直接使用 gcc
命令需要添加 -fPIC (-fpic)
以及 -shared
参数,其作用时使得 gcc 生成的代码与位置无关,也就是使用相对位置, -shared
参数是 告诉
编译器生成一个 动态链接库
# 生成动态链接库
- 将源文件进行汇编操作,需要使用
-c
,还需要添加额外参数 -fpic / -fPIC
| |
| gcc 源文件(*.c) -c -fpic/ |
| |
| gcc -shared 有关的目标文件(*.o) -o 动态库(libxxx.so) |
| |
| |
| |
# 无法加载问题
将库路径添加到环境变量 LD_LIBRARY_PATH
中
# 方法一
1, 找到相关的配置文件
| |
| export LIBRARY_PATH=$LIBRARY_PATH:动态库的绝对路径 |
3, 让配置文件生效
- 修改用户级别的配置文件,关闭当前终端,即可
- 系统级别,重启
- 或者
| source 作用让文件内容被重新加载 |
| source ~/.bashrc (. ~/.bashrc) |
| source /etc/profile (. /etc/profile) |
# 方法二
更新 /etc/ld.so.cache
文件
- 找到动态库所在的绝对路径:
/home/robin/Library/
- 使用 vim 修改
/etc/ld.so.conf
文件,将上面的路径添加,独占一行 - 更新
/etc/ld.so.conf
中的数据到 /etc/ld/so.cache
中
# 方法三
拷贝拷贝动态库文件到系统库目录 /lib/
或者 /usr/lib
中 (或者将库的软链接文件放进去)
| |
| sudo cp /xxx/xxx/libxxx.so /usr/lib |
| |
| sudo ln -s /xxx/xxx/libxxx.so /usr/lib/libxxx.so |
# 原理
静态库:在程序编译的最后一个阶段 -- 链接阶段,提供静态库被打包到可执行程序中,当可执行程序执行,静态库中的代码也会一并加载到内存中
动态库:使用参数 - L 查看库文件是否存在。在程序执行先检测是否动态库被加载,当动态库的函数在程序中被调用,这个动态库被加载到内存,不掉用不加载。动态库的检测和内存操作都是由动态连接器来完成
# 对比
- 静态库:
- 优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
- 缺点:
- 相同的库文件数据被加载多份,消耗资源,浪费内存
- 库文件更新需要重新编译项目文件,生成新的可执行程序浪费时间
- 动态库
- 优点
- 不同进程资源共享
- 动态库升级,只需替换库文件,可以控制何时加载动态库
- 缺点:
- 加载速度慢,
- 发布程序需要依赖动态库
# Makefile
make 是一个命令工具,是一个解释 makefile 中指令的命令工具。
Makefile--- 自动化编译,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
# 规则
| target1,target2...: depend1,depend2, ... |
| command |
| ...... |
command
:命令,一般情况下这个动作就是一个 shell 命令depend
: 规则所必需的依赖条件,target
: 目标,目标和规则在命令中是对应的。
在调用 Makefile
命令编译程序时, make
会首先找到 Makefile 文件中的第一个规则,分析并执行相关的动作
根据上面的代码
运行结果
# 自动推导
当源文件的编译规则 不明确
时,make 会使用 默认
的编译规则,按照默认规则完成对 .c
文件你的编译, 生成
对应的 .o
文件,它使用 cc -c
编译 .c
源文件。
# 变量
为了使 makefile
进行规则设定时更加的 灵活
。
# 自定义变量
在对 makefile 进行规则设定时,用户可以自己定义变量,用户自定义变量。其变量没有类型,直接赋值即可
# 自定义变量自动推导
| obj = add.o sub.o mult.o div.o main.o |
| target = calc |
| $(target) : $(obj) |
| gcc $(obj) -o $(target) |
# 预定义变量
此变量为 Makefile 已经预先定义的,变量名 都是大写,可以直接使用这些变量。
变 量 名 | 含 义 | 默 认 值 |
---|
AR | 生成静态库库文件的程序名称 | ar |
AS | 汇编 编译器的名称 | as |
CC | C 语言编译器的名称 | cc |
CPP | C 语言预编译器的名称 | $(CC) -E |
CXX | C++ 语言编译器的名称 | g++ |
FC | FORTRAN 语言编译器的名称 | f77 |
RM | 删除文件程序的名称 | rm -f |
ARFLAGS | 生成静态库库文件程序的选项 | 无默认值 |
ASFLAGS | 汇编语言编译器的编译选项 | |
CFLAGS | C 语言编译器的编译选项 | |
CPPFLAGS | C++ 语言编译器的编译选项 | |
FFLAGS | FORTRAN 语言编译器的编译选项 | |
# 自动变量
自动变量用来代表这些规则中的目标文件和依赖文件,并且他们只能在规则的命令中使用
变量 | 含 义 |
---|
$* | 表示目标文件的名称,不包含目标文件的扩展名 |
$+ | 表示所有的依赖文件,这些依赖文件之间以空格分开,按照出现的先后为顺序,其中可能 包含重复的依赖文件 |
$< | 表示依赖项中第一个依赖文件的名称 |
$? | 依赖项中,所有比目标文件时间戳晚的依赖文件,依赖文件之间以空格分开 |
$@ | 表示 目标文件 的 名称 ,包含 文件扩展名 |
$^ | 依赖项中,所有 不重复 的 依赖文件 ,这些文件之间以 空格 分开 |
| calc2 : add.o sub.o mult.o div.o main.o |
| $(CC) $^ -o $@ |
# 模式匹配
模式匹配相当于一个模版,第一个规则中依赖的生成,都需要基于此规则来完成,得到所有的依赖,模式规则被执行了 5 次,模式规则中 %
对应的文件名是 时时变化
的,因此命令的依赖的名字,必须要使用 自动变量
。
| calc2 : add.o sub.o mult.o div.o main.o |
| $(CC) $^ -o $@ |
| %.o:%.c |
| gcc &< -c |
# 函数
函数格式为: $(函数名 参数1,参数2,...)
目的为快速方便的得到 函数返回值
# withcard 获取文件名
# 应用案例
搜索三个不同目录下 .c
格式的源文件
| src = $(withcard /home/robin/a/*.c /home/robin/b/*.c *.c) |
| |
# patsubst 替代指定文件后缀
按照 指定的模式
替换指定的文件名的后缀,函数原型为
| $(patsubst <pattern>,<replacement>,<text>) |
- 参数
pattern
:需要指定要替换的文件名的 后缀
是什么,使用 %.c
replacement
:模式投资富川,指定参数 pattern
中的后缀, 最终要替换
为什么。text
: 该参数中存储这要被替换的原始数据返回值
:该参数中存储这要被替换的原始数据
| src = a.cpp b.cpp c.cpp e.cpp |
| |
| obj = $(patsubst %.cpp,%.o,$(src)) |
| |
# Makefile 完整案例
| |
| src = $(wildcard *.c) |
| |
| obj = $(patsubst %.c,%.o,$(src)) |
| |
| target = calc |
| |
| $(target):$(obj) |
| gcc $(obj) -o $(target) |
| %.o : %.c |
| gcc $< -c |
| |
| |
| |
| .PHONY:clean |
| |
| clean: |
| |
| - rm $(obj) $(target) |
| echo "hello" |
参考资料:
爱编程的大丙