C and Cpp Compilation
gcc 与 g++
| 工具 | 常见用途 | 说明 |
|---|---|---|
gcc |
编译 C 程序 | 也能根据后缀调用对应前端,但链接 C++ 程序时不如 g++ 省心 |
g++ |
编译 C++ 程序 | 会自动链接 C++ 标准库 |
经验规则:
- C 文件:
gcc main.c -o main - C++ 文件:
g++ main.cpp -o main
从源码到可执行文件的四个阶段
C/C++ 源码变成可执行程序,一般分为四步:预处理、编译、汇编、链接。
1 | .c/.cpp 源码 |
1. 预处理 Preprocessing
1 | gcc -E file.c -o file.i |
预处理会:
- 展开
#include文件; - 替换
#define宏; - 处理条件编译,如
#ifdef/#ifndef/#if。
输出结果是已经展开宏和头文件的文本文件。
2. 编译 Compilation
1 | gcc -S file.i -o file.s |
编译阶段把预处理后的 C/C++ 代码翻译成汇编代码,输出 .s 文件。
3. 汇编 Assembling
1 | gcc -c file.s -o file.o |
汇编阶段把 .s 汇编代码转换成目标文件 .o。目标文件已经包含机器码,但通常还不能直接运行,因为外部函数、全局符号、库函数等引用还没完全解析。
常用的“一步生成目标文件”写法:
1 | gcc -c file.c -o file.o |
4. 链接 Linking
1 | gcc file.o -o program |
链接阶段会:
- 合并多个
.o文件; - 链接静态库或动态库;
- 解析函数调用、全局变量等符号地址;
- 生成最终可执行文件。
多个源文件的常见编译方式:
1 | g++ -c main.cpp -o main.o |
常见组合命令
一步到位编译:
1 | gcc file.c -o program |
只预处理:
1 | gcc -E file.c -o file.i |
只生成汇编:
1 | gcc -S file.c -o file.s |
只编译不链接:
1 | gcc -c file.c -o file.o |
常用编译选项
| 选项 | 描述 |
|---|---|
-o <output_file> |
指定输出文件名;省略时通常输出 a.out |
-std=c++17 / -std=c11 |
指定语言标准 |
-O0 |
不优化,适合调试 |
-O2 |
常用优化级别,兼顾性能和稳定性 |
-O3 |
更激进优化,可能增加体积,也不一定总更快 |
-Os |
优化体积 |
-Wall |
启用大部分常用警告 |
-Wextra |
启用额外警告 |
-Werror |
把警告当作错误 |
-g / -ggdb |
生成调试信息,便于 GDB 调试 |
-I<dir> |
添加头文件搜索路径 |
-L<dir> |
添加库文件搜索路径 |
-l<name> |
链接库,例如 -lm 链接数学库 |
-DNAME=value |
在编译命令中定义宏 |
-fstack-protector-all |
启用栈保护,尝试检测栈缓冲区溢出 |
开发调试阶段常用:
1 | g++ -std=c++17 -O0 -g -Wall -Wextra -Werror -fsanitize=address,undefined main.cpp -o main |
竞赛/发布阶段常用:
1 | g++ -std=c++17 -O3 -Wall -Wextra main.cpp -o main |
Sanitizer
AddressSanitizer 和 UndefinedBehaviorSanitizer 很适合开发期排错。
1 | g++ -g -O1 -fsanitize=address -fno-omit-frame-pointer main.cpp -o main |
1 | g++ -g -O1 -fsanitize=undefined main.cpp -o main |
| 选项 | 检测内容 |
|---|---|
-fsanitize=address |
越界访问、use-after-free 等内存访问错误 |
-fsanitize=undefined |
未定义行为,如部分整数溢出、空指针解引用等 |
注意:Sanitizer 会增加编译时间和运行时开销,通常只在开发、测试、调试阶段使用。
常见错误定位
undefined reference
示例:
1 | undefined reference to `foo()` |
通常发生在链接阶段,表示声明存在,但链接器找不到实现。常见原因:
- 忘记把实现所在的
.cpp/.o加入编译命令; - 忘记链接库;
- 函数签名声明和定义不一致;
- C 与 C++ 混合链接时缺少
extern "C"。
multiple definition
示例:
1 | multiple definition of `x` |
通常表示同一个全局变量或函数被多个目标文件重复定义。常见原因:
- 在头文件中直接定义了非
inline函数或全局变量; - 头文件被多个
.cpp包含后,每个.cpp都生成一份定义。
解决思路:
- 头文件里放声明:
extern int x; - 某一个
.cpp里放定义:int x = 0; - 函数模板、类模板通常需要放头文件;普通函数不要随便在头文件中定义,除非标记为
inline。
查看更详细帮助
1 | man gcc |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Elian's blog page!