编译过程小结
简单总结编译过程。
编译过程
gcc/g++ [-o filename] source.c/cpp
功能:将源程序文件转化为可执行文件
过程:预处理 -> 编译 -> 汇编 -> 链接
预处理
gcc -E demo.c -o demo.i
- 处理宏定义指令
- 处理条件编译指令
- 扩展头文件包含指令
- 注释、空行被删除
未定义声明的函数,在预处理过程不会报错
// demo.c
#include <stdio.h>
#defind NUM 1
int main()
{
char* name = "hello";
// 注释
int num = NUM;
printf("%s\n", name);
return 0;
}
编译
gcc -S demo.i -o demo.s
- 将预处理代码转化为汇编代码
汇编
gcc -c demo.s -o demo.o
- 将汇编代码转化为目标代码(Linux下为ELF文件,Windows下为PE文件
objdump -s -d demo.o
查看ELF文件结构
目标代码还不能执行,仅有函数声明,函数的实现过程未知。
链接
gcc demo.o -o demo
- 纳入函数/变量的定义实现
- 静态链接:将实现一起打包生成可执行文件
- 动态链接:运行时动态加载
函数库
- 现有的、成熟的、可复用的代码
- 根据链接方式,可分为
- 静态库:.a(Linux,.lib(Windows
- 动态库:.so(Linux,.dll(Windows
静态库
- 一组目标文件(.o/.obj文件)的集合
- 与汇编生成的目标文件.o一起链接打包到可执行文件
- 特点
- 链接是在编译阶段完成
- 程序运行时与函数库再无瓜葛,移植方便
- 可执行文件巨大,包含所有相关目标文件与涉及函数库
动态库
- 静态库不足:巨大的可执行文件、全量更新
- 动态库:编译时不会链接到目标代码,在程序运行时动态加载
Linux下静态库创建
// add.h
int add(int a, int b);
// add.c
#include <add.h>
int add(int a, int b)
{
return a+b;
}
# 编译生成目标文件
gcc -c add.c -I . -o add.o // -I 表示将当前目录纳入系统头文件搜索路径
# 生成静态连接库
ar -crv libadd.a add.o
- -c:创建静态库
- -r:将目标文件加入静态库
- -v:显示处理信息
静态链接:
// main.cc
#include <add.h>
#include <stdio.h>
int main()
{
printf("%d\n", add(1,2));
return 0;
}
gcc -c main.c -I . -o main.o
# 静态链接
gcc main.o -L . -ladd -o main.out // -L表示将当前目录纳入系统函数库搜索路径
Linux下动态库的创建
# 编译生成动态库
gcc -fPIC -shared add.c -I . -o libadd.so
- -fPIC:创建与地址无关的程序
- -shared:生成动态链接库
在链接之前需增加动态链接库搜索路径
sudo vim /etc/ld.so.conf
sudo ldconfig
# 动态链接
gcc -c main.c -I. -o main.o
gcc main.o -L. -ladd -o main.out