编译过程
预处理 -> 编译 -> 汇编 -> 链接
预处理
- 完成宏替换、文件引入,去除空行、注释等。
- 针对预处理命令进行处理,头文件包含、宏定义扩展、条件编译的选择等。
# test.c
#include <stdio.h>
int main(){
printf("hello world!\n");
return 0;
}
------------------------------------------------
$ gcc -E test.c -o test.i
- -E: 让gcc在预处理结束后停止编译,test.i文件为预处理后的文件
- -o: 指定输出文件
编译
这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。
$ gcc -S test.i -o test.s
汇编
将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。
$ gcc -c test.s -o test.o
链接
链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件
$ gcc test.o -o test
$ ./test
更详细的内容可以参考:https://www.cnblogs.com/CarpenterLee/p/5994681.html#top
生成静态库
- 首先生成test.o目标文件
- 使用ar命令将test.o打包成libtest.a静态库
# 首先生成目标文件
$ gcc -c test.c -o test.o
# 使用ar命令将目标文件打包成静态库
$ ar rcs libtest.a test.o
# 使用ar t libtest.a 查看静态库内容
$ ar t libtest.a
test.o
生成动态库
- 首先生成test.o目标文件。
- 使用-shared和-fPIC参数生成动态库
# 首先生成目标文件
$ gcc -c test.c
# 使用-fPIC和-shared生成动态库
$ gcc -shared -fPIC -o libtest.so test.o
例子
tool.h
int find_max(int arr[], int n);
tool.c
#include "tool.h"
int find_max(int arr[], int n){
int max = arr[0];
int i;
for(i = 0; i < n; i++){
if(arr[i] > max){
max = arr[i];
}
}
return max;
}
main.c
#include <stdio.h>
#include "tool.h"
int main(){
int arr[] = {1,3,5,8,2};
int max = find_max(arr, 5);
printf(" max = %d\n", max);
return 0;
}
编译静态库
- 编译目标文件
$ gcc -c tool.c
,省略-o默认生成同名文件 - 编译静态库文件
ar rcs libtool.a tool.o
- 编译可执行文件&链接静态库
gcc -o main main.c -L. -ltool
-L: 查询库路径
-l: 需要链接的库 - 执行
./main
编译动态库
$ gcc -shared -fPIC -o libtool.so tool.c
$ gcc -o main -L. -ltool main.c
$ ./main
ps
- 静态库和动态库同名时,系统会优先链接动态库。
- 查看文件详情
ls -lh
- 查看链接库详情
linuxldd main
macOx 动态库:otool -L main
,静态库nm main
静态库与动态库区别
-
静态库
在程序编译时会链接到目标代码中,程序运行时不在需要静态库,因此体积较大。每次编译都需要载入静态代码,因此内存开销大。 -
动态库
在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,程序运行时需要动态库存在,因此体积较小。而且系统只需载入一次动态库,不同程序可以得到内存中相同的动态库副本,因此内存开销小。