编译过程小结

简单总结编译过程。

编译过程

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;
}
image-20201121220854587

编译

gcc -S demo.i -o demo.s

  • 将预处理代码转化为汇编代码
image-20201121221030499

汇编

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一起链接打包到可执行文件
  • 特点
    • 链接是在编译阶段完成
    • 程序运行时与函数库再无瓜葛,移植方便
    • 可执行文件巨大,包含所有相关目标文件与涉及函数库

动态库

  • 静态库不足:巨大的可执行文件、全量更新
  • 动态库:编译时不会链接到目标代码,在程序运行时动态加载
image-20201121221435132

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