参考课程
参考书目:
gcc/g++编译命令
基本编译流程:预处理 → 编译 → 汇编 → 链接
- 预处理:处理宏、头文件、条件编译等
- 展开所有宏定义
- 处理所有
#include指令
- 处理条件编译指令(
#ifdef, #ifndef, #if, #else, #endif)
- 删除所有注释
- 添加行号和文件名标识(可使用
-P去除)
- 保留
#pragma指令
- 编译:将预处理后的代码转换为汇编语言
- 词法分析和语法分析
- 语义分析
- 中间代码生成
- 代码优化
- 生成目标平台的汇编代码
- 汇编:将汇编代码转换为机器码(目标文件)
- 将汇编指令翻译为机器指令
- 生成可重定位的目标文件(.o文件)
- 生成符号表
- 处理标签和地址引用
- 链接:将多个目标文件和库文件连接成可执行文件
- 符号解析(解决未定义的引用)
- 重定位(调整地址引用)
- 合并代码和数据段
- 链接启动代码和库文件
- 生成最终的可执行文件格式
命令选项
| 选项 |
描述 |
示例 |
-o <file> |
指定输出文件名 |
gcc -o program main.c |
-E |
只预处理,不编译 |
gcc -E main.c -o main.i |
-S |
生成汇编代码 |
gcc -S main.c -o main.s |
-c |
编译生成机器码不链接 |
gcc -c main.c -o main.o |
-save-temps |
保存所有中间文件 |
gcc -save-temps main.c -o program |
-pipe |
使用管道代替临时文件 |
gcc -pipe main.c -o program |
优化级别选项:
| 选项 |
描述 |
效果 |
-O0 |
不优化(默认) |
编译快,调试方便 |
-O1 |
基本优化 |
减少代码大小和执行时间 |
-O2 |
更多优化(推荐) |
包括所有不涉及空间换时间的优化 |
-O3 |
激进优化 |
包括向量化等高级优化 |
-Os |
优化代码大小 |
优先减小可执行文件大小 |
-Ofast |
快速优化 |
可能违反严格标准 |
-Og |
优化调试体验 |
在保持可调试性的同时优化 |
如果使用了优化选项:
- 编译的时间将更长;
- 目标程序不可调试;
- 有效果,但是不可能显著提升程序的性能。(关键还是要看源代码的实现)
目标文件 vs 可执行文件
| 特性 |
目标文件 (.o/.obj) |
可执行文件 |
| 文件扩展名 |
.o (Unix/Linux) .obj (Windows) |
无扩展名 (Linux) .exe (Windows) |
| 文件类型 |
可重定位目标文件 |
可执行目标文件 |
| 链接状态 |
未链接 |
已完全链接 |
| 能否直接运行 |
❌ 不能直接执行 |
✅ 可以直接运行 |
| 地址引用 |
相对地址/未解析符号 |
绝对地址/已解析符号 |
| 包含内容 |
单个模块的代码和数据 |
完整程序的所有代码和数据 |
| 段结构 |
需要重定位的段 |
加载到内存的最终段 |
| 依赖关系 |
依赖其他目标文件/库 |
可能依赖动态库,但独立运行 |
示例:将hello.c编译为可执行文件hello
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello
./hello
gcc hello.c -o hello_one_step
|
静态库、动态库
一般来讲,通用的函数和类不提供源代码文件,而是编译成二进制文件。
库的二进制文件:
如果动态库和静态库同时存在,编译器将优先使用动态库。
静态库
- 文件扩展名:
.a (Unix/Linux), .lib (Windows)
- 本质: 多个目标文件(
.o)的归档文件
- 链接时机: 编译/链接时
- 特点:
- 程序在编译时会把库文件的二进制代码链接到目标程序中
- 库代码被复制到最终的可执行文件中
- 如果多个程序中用到了同一静态库中的函数或类,就会存在多份拷贝
- 静态库的链接是在编译时期完成的,执行的时候代码加载速度快。
- 生成的目标程序的可执行文件比较大,浪费空间。
- 程序的更新和发布不方便,如果某一个静态库更新了,所有使用它的程序都需要重新编译
创建方式
1
| g++ -c -o lib库名.a 源代码文件清单
|
1 2
| g++ -c -o libpublic.a public.cpp
|
使用方式
1
| g++ 选项 源代码文件名清单 -l库名 -L库文件所在的目录名
|
1
| g++ -o demo01 demo01.cpp -lpublic -L/home/wucz/tools
|
动态库
- 文件扩展名:
.so (Unix/Linux), .dll (Windows), .dylib (macOS)
- 本质: 独立的可执行代码模块
- 链接时机: 运行时
- 特点:
- 可执行文件只包含引用,运行时动态加载
- 如果多个进程中用到了同一动态库中的函数或类,那么在内存中只有一份,避免了空间浪费问题
- 程序在运行的过程中,需要用到动态库的时候才把动态库的二进制代码载入内存。
- 可以实现进程之间的代码共享,因此动态库也称为共享库。
- 程序升级比较简单,不需要重新编译程序,只需要更新(重新制作)动态库就行了。
创建方式
1
| g++ -fPIC -shared -o lib库名.so 源代码文件清单
|
1 2
| g++ -fPIC -shared -o libpublic.so public.cpp gcc -fPIC -shared -o libmath.so mathlib.c
|
| 选项 |
全称 |
含义 |
作用阶段 |
-fPIC |
Position Independent Code |
生成位置无关代码 |
编译阶段 |
-shared |
Shared Library |
生成共享库(动态库) |
链接阶段 |
使用方式
运行可执行程序的时候,需要提前设置LD_LIBRARY_PATH环境变量,即指定动态库的目录
1
| g++ 选项 源代码文件名清单 -l库名 -L库文件所在的目录名
|
1
| g++ -o demo01 demo01.cpp -lpublic -L/home/wucz/tools
|
makefile