GCC 编译器使用与调试基础指南
简介
1984 年,Richard Stallman 发起自由软件运动,GNU 项目随之诞生。三年后,第一款可移植、支持 ANSI C 的开源 C 编译器——GCC(GNU Compiler Collection)问世。虽然最初名为 GNU C Compiler,但随着支持语言增多,现在通常指代整个编译工具集。
常用编译警告选项
开启警告是发现潜在问题的第一步。建议开发阶段始终加上 -Wall:
-Wall:显示所有常用的编译警告信息。-W:显示更多警告,如未使用变量或逻辑错误。-Wconversion:警告隐式类型转换。-Wshadow:警告影子变量(在作用域内重复声明已存在的变量)。-Wcast-qual:警告指针修改了变量的修饰符(例如指向 const 的指针被修改)。-Wwrite-strings:警告尝试修改 const 字符串。-Werror:将警告视为错误,即使只有警告也不编译。默认情况下 gcc 遇到警告仍会编译,除非显式指定此选项。
指定 C 语言标准
通过命令行选项控制 C 标准版本,从传统 C 到最新的 GNU 扩展:
-ansi:关闭 GNU 扩展中与 ANSI C 抵触的部分。-pedantic:关闭所有 GNU 扩展,严格遵循标准。-std=c89/-std=c99:分别遵循 C89 或 C99 标准。-std=traditional:使用原始 C。
注意:后四个选项可与 -ansi 结合使用,也可单独使用。默认情况下,gcc 使用最新的 GNU C 扩展。
编译过程与中间文件
以 hello.c 为例,gcc 执行流程如下:
- 预处理 (
hello.i):展开宏、包含头文件。 - 编译 (
hello.s):生成汇编代码。 - 汇编 (
hello.o):生成目标文件(机器码,但符号未解析)。 - 链接 (
hello或a.out):生成最终可执行文件。
若不通过 -o 指定文件名,默认生成 a.out。不指定文件名可能会覆盖上次的输出,建议养成指定习惯。
$ gcc hello.c
上述命令执行默认操作:源文件 -> 目标文件 -> 可执行文件 -> 删除目标文件。
若只生成目标文件:
$ gcc -Wall -c hello.c # 生成 hello.o
$ gcc -Wall -c -save-temps hello.c # 同时保留 .i, .s, .o
多文件编译与链接
当项目分布在多个文件中时(如 file1.c, file2.c):
$ gcc -Wall file1.c file2.c -o name
若仅修改其中一个文件,可只重新编译该文件再链接:
$ gcc -Wall -c file2.c
$ gcc file1.c file2.o -o name
注意:虽然 GCC 对 .o 文件的顺序限制较少,但含有函数定义的文件最好放在调用该函数的文件之后,以防链接器报错。
编译预处理详解
预处理器由 cpp 程序处理,可通过 gcc 的 -E 选项调用:
$ gcc -E hello.c # 等同于 $ cpp hello.c
$ gcc -E -c hello.i # 保存预处理结果
命令行定义宏也很常用:
$ gcc -Wall -DVALUE="2+2" tmp.c -o tmp
如果不赋值,默认为 1。编译器还会自动定义一些宏(以 __ 开头),运行 cpp -dM /dev/null 可查看。
查看宏扩展
- 直接运行
$ gcc -E test.c显示结果。 - 使用
$ gcc -c -save-temps test.c生成.i文件保存预处理结果。 - Emacs 用户可使用
C-c C-e(Macroexpand) 快速查看选中代码的预处理结果。
头文件包含与搜索路径
使用库时必须正确引用头文件。系统默认路径通常为 /usr/include 和 /usr/local/include。
#include <file.h>:仅在系统默认路径搜索。#include "file.h":先在当前目录搜索,找不到再去系统路径。
若自定义头文件不在默认路径,可通过 -I 指定:
$ gcc -I./include main.c
优先级顺序为:命令行设置 > 环境变量设置 > 系统默认。环境变量如 C_INCLUDE_PATH 可用冒号分隔多个路径。
外部库链接策略
链接库时,需确保库文件位于连接路径中。C 标准库 (libc.a) 会自动加载,其他库需显式指定。
静态库 (.a) 与共享库 (.so)
- 静态库:代码复制到可执行文件中,体积大,独立性强。
- 共享库:仅记录引用,运行时加载,节省空间,便于更新。
示例:计算平方根需要链接数学库 libm。
$ gcc -Wall sqrt.c -lm -o sqrt
注意链接顺序:包含函数定义的库文件必须位于调用该函数的目标文件之后。如果顺序反了,链接器可能报 undefined reference。
强制静态链接
若不想用共享库,可加 -static 选项:
$ gcc -Wall -static hello.c -lNAME -o hello
创建库
使用 ar 命令创建静态库:
$ ar cr libhello.a hello_fn.o by_fn.o
$ ar t libhello.a # 列出内容
调试信息生成
默认生成的可执行文件不包含源代码映射信息。添加 -g 选项可嵌入调试符号:
$ gcc -Wall -g hello.c -o hello
若发生段错误且无 core dump,可能是系统限制了核心文件生成。检查并设置:
$ ulimit -c unlimited
可在 ~/.bashrc 中永久生效。
代码优化选项
GCC 提供多种优化级别,权衡速度与空间:
-O0:默认不优化(调试时推荐)。-O1:简单优化。-O2:进一步优化,包括调度(发布默认级别)。-O3:激进优化,有时反而变慢。-Os:生成最小体积文件(适合嵌入式)。
注意:优化选项 -O 是大写字母 O,而非数字 0 或小写 o。测试优化效果可用 time ./prog 命令。
提示:优化可能会引发新的警告,解决方式应是调整代码而非关闭优化。

