强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

GCC 完全指南 / 04 - 常用编译选项

04 - 常用编译选项

全面掌握 GCC 最常用的编译选项——优化、警告、标准选择、路径指定、预定义宏等。


4.1 选项分类概览

GCC 选项数量庞大(数百个),但日常开发中常用的约 30-50 个。按功能可分为以下几类:

类别常用选项说明
输出控制-o, -E, -S, -c控制输出文件名和编译阶段
优化-O0, -O1, -O2, -O3, -Os控制优化级别
警告-Wall, -Wextra, -Werror, -Wpedantic控制警告信息
标准选择-std=c11, -std=c++17选择语言标准
调试-g, -g1, -g2, -g3生成调试信息
预定义-D, -U定义/取消宏
头文件路径-I, -isystem指定头文件搜索路径
库路径-L, -l指定库路径和库名称
代码生成-fPIC, -fPIE, -fno-common控制代码生成方式
架构-march, -mtune, -msse, -mavx目标架构和指令集

4.2 输出控制选项

# 指定输出文件名
gcc -o hello main.c            # 输出 hello

# 不指定 -o 时的默认名
gcc main.c                     # 输出 a.out(可执行文件)
gcc -c main.c                  # 输出 main.o(目标文件)
gcc -S main.c                  # 输出 main.s(汇编文件)
gcc -E main.c                  # 输出到标准输出
gcc -E -o main.i main.c       # 输出到文件

# 编译多个文件
gcc -o hello main.c greet.c   # 一步完成
gcc -c main.c greet.c         # 生成 main.o 和 greet.o

4.3 优化选项

选项优化级别说明适用场景
-O0无优化默认值,编译最快,调试最准开发调试
-O1基本优化减小代码大小和执行时间,不增加编译时间一般开发
-O2推荐优化几乎所有安全的优化,推荐用于生产生产构建
-O3激进优化在 -O2 基础上增加循环展开、向量化等性能关键场景
-Os大小优化优化代码大小,适合嵌入式和存储受限环境嵌入式/容器
-Og调试友好优化不影响调试的优化,兼顾性能和调试日常开发
-Ofast极限优化-O3 加上可能违反标准的优化(如 -ffast-math科学计算
# 不同优化级别的对比
for opt in O0 O1 O2 O3 Os Ofast; do
    gcc -$opt -o hello_$opt main.c greet.c
    size hello_$opt
done

# 输出示例:
#    text    data     bss     dec     hex filename
#    1640     560       8    2208     8a0 hello_O0
#    1576     552       8    2136     858 hello_O1
#    1544     552       8    2104     838 hello_O2
#    1544     552       8    2104     838 hello_O3
#    1512     544       8    2064     810 hello_Os
#    1544     552       8    2104     838 hello_Ofast

-Og 的特殊地位

# -Og 是"日常开发"的最佳选择
gcc -g -Og -o hello main.c

# 为什么不推荐 -O0 + -g?
# -O0 完全不优化,某些变量优化后不存在,调试时体验差
# -Og 在不影响调试的前提下做基本优化,更贴近实际运行状态

-Ofast 的风险

# -Ofast 包含 -ffast-math,可能导致浮点计算结果不精确
# 例如: NaN 比较、无穷大处理可能不同
# 仅适用于对浮点精度要求不严格的科学计算场景

4.4 警告选项

# 启用常见警告(推荐始终使用)
gcc -Wall -o hello main.c

# 启用更多额外警告
gcc -Wall -Wextra -o hello main.c

# 将所有警告视为错误(CI/CD 中常用)
gcc -Wall -Wextra -Werror -o hello main.c

# 建议性的额外警告(严格标准遵从)
gcc -Wall -Wextra -Wpedantic -o hello main.c

常用警告选项详解

选项说明
-Wall启用大部分常见警告(不是全部!)
-Wextra启用 -Wall 未包含的额外警告
-Wpedantic严格遵循语言标准,对非标准扩展发出警告
-Werror将所有警告视为错误
-Werror=<warning>将特定警告视为错误
-Wno-<warning>禁用特定警告
-Wshadow变量遮蔽警告
-Wconversion隐式类型转换警告
-Wformat=2printf/scanf 格式字符串检查
-Wnull-dereference空指针解引用警告
-Wdouble-promotionfloat 隐式提升为 double
# 禁用特定警告
gcc -Wall -Wno-unused-variable -o hello main.c

# 将特定警告升级为错误
gcc -Wall -Werror=return-type -o hello main.c

4.5 语言标准选择

C 语言标准

# C89/C90
gcc -std=c89 -o hello main.c
gcc -ansi -o hello main.c       # 等价于 -std=c89

# C99
gcc -std=c99 -o hello main.c

# C11
gcc -std=c11 -o hello main.c

# C17
gcc -std=c17 -o hello main.c

# C23(GCC 13+)
gcc -std=c23 -o hello main.c

# GNU 扩展模式(默认)
gcc -std=gnu17 -o hello main.c  # C17 + GNU 扩展

# 查看默认标准
gcc -dM -E - < /dev/null | grep __STDC_VERSION__
# #define __STDC_VERSION__ 201710L  (表示 C17)

C++ 标准

# C++11
g++ -std=c++11 -o hello main.cpp

# C++14
g++ -std=c++14 -o hello main.cpp

# C++17
g++ -std=c++17 -o hello main.cpp

# C++20
g++ -std=c++20 -o hello main.cpp

# C++23(GCC 13+)
g++ -std=c++23 -o hello main.cpp

# 查看默认标准
g++ -dM -E -x c++ /dev/null | grep __cplusplus
# #define __cplusplus 201703L  (表示 C++17)

标准选择建议

场景推荐标准理由
新 C 项目-std=c17成熟稳定,广泛支持
新 C++ 项目-std=c++17主流选择,特性丰富
前沿 C++-std=c++20 / -std=c++23协程、模块、格式化等新特性
兼容旧系统-std=c99 / -std=c++11最广泛的兼容性
Linux 内核-std=gnu11内核需要 GNU 扩展

4.6 头文件路径选项

# 指定头文件搜索路径
gcc -I/usr/local/include -I./include -o hello main.c

# 多个路径可以连写
gcc -I./include -I../common/include -I/usr/local/include -o hello main.c

# 使用系统搜索路径(视为系统头文件,不产生警告)
gcc -isystem /usr/local/include -o hello main.c

# 使用引号搜索路径(对 #include "file.h" 的搜索顺序)
gcc -iquote ./include -o hello main.c

# 头文件搜索顺序(对于 #include <file.h>):
# 1. -I 指定的目录(按顺序)
# 2. -isystem 指定的目录
# 3. 系统默认目录(/usr/include 等)

# 头文件搜索顺序(对于 #include "file.h"):
# 1. 当前源文件所在目录
# 2. -iquote 指定的目录
# 3. -I 指定的目录
# 4. -isystem 指定的目录
# 5. 系统默认目录

查看搜索路径

# 查看默认头文件搜索路径
gcc -v -E -x c /dev/null 2>&1 | sed -n '/#include.*search starts/,/End of search/p'

# 或
cpp -v /dev/null 2>&1 | sed -n '/#include.*search/,/End of/p'

4.7 库路径与库链接

# 指定库搜索路径
gcc -L/usr/local/lib -L./lib -o hello main.c

# 链接特定库
# -l<name> 会搜索 lib<name>.so(动态库)或 lib<name>.a(静态库)
gcc -lm -lpthread -o hello main.c         # 链接 libm 和 libpthread
gcc -L./lib -lmylib -o hello main.c       # 链接 libmylib.so 或 libmylib.a

# 指定静态链接某个库
gcc -Wl,-Bstatic -lmylib -Wl,-Bdynamic -o hello main.c

# 链接时链接顺序很重要!被依赖的库放在后面
gcc main.o -lmylib -lm -o hello    # ✅ 正确
gcc -lmylib -lm main.o -o hello    # ❌ 可能报 undefined reference

# 查看链接的库
ldd hello

# 强制链接未使用的库
gcc -Wl,--no-as-needed -lmylib -o hello main.c

库链接顺序规则

链接顺序原则:
  gcc main.o -lA -lB -lc

  - main.o 中引用了 libA 中的符号
  - libA 中引用了 libB 中的符号
  - libB 中引用了 libc 中的符号

  链接器从左到右扫描:
  1. 扫描 main.o → 记录未解析符号
  2. 扫描 libA → 解析 main.o 的符号,记录 libA 的未解析符号
  3. 扫描 libB → 解析 libA 的符号,记录 libB 的未解析符号
  4. 扫描 libc → 解析 libB 的符号

  如果顺序反了:
  gcc -lB -lA main.o
  扫描 libB 时没有未解析符号,libB 的内容不会被链接
  → 链接器不会回溯搜索 → 报 undefined reference

4.8 预定义宏选项

# 定义宏(等价于源码中的 #define DEBUG 1)
gcc -DDEBUG=1 -o hello main.c

# 定义不带值的宏(等价于 #define DEBUG)
gcc -DDEBUG -o hello main.c

# 取消宏定义(等价于 #undef DEBUG)
gcc -UDEBUG -o hello main.c

# 常见用法
gcc -DVERSION=\"1.0.0\" -DBUILD_DATE=\"2026-05-10\" -o hello main.c
gcc -DNDEBUG -o hello main.c           # 禁用 assert()
gcc -D_GNU_SOURCE -o hello main.c      # 启用 GNU 扩展

在代码中使用

#include <stdio.h>

int main(void) {
#ifdef DEBUG
    printf("Debug mode enabled\n");
#endif

    printf("Version: %s\n", VERSION);
    printf("Build date: %s\n", BUILD_DATE);

    return 0;
}
gcc -DDEBUG -DVERSION=\"1.0.0\" -DBUILD_DATE=\"2026-05-10\" -o hello main.c
./hello
# Debug mode enabled
# Version: 1.0.0
# Build date: 2026-05-10

4.9 代码生成选项

选项说明
-fPIC生成位置无关代码(Position Independent Code),用于共享库
-fPIE生成位置无关可执行文件,ASLR 安全加固(默认开启)
-fno-common不允许未初始化的全局变量有多个定义(GCC 10+ 默认)
-fcommon允许未初始化的全局变量有多个定义(GCC 9 及之前默认)
-fpic类似 -fPIC,但对偏移量有限制(某些架构更高效)
-fstack-protector启用栈保护(-fstack-protector-strong 推荐)
-fno-omit-frame-pointer保留帧指针(便于调试和性能分析)
-fasynchronous-unwind-tables生成异步展开表(默认开启,用于 backtrace)
# 编译共享库
gcc -fPIC -shared -o libhello.so hello.c

# 栈保护(推荐生产使用)
gcc -fstack-protector-strong -o hello main.c

# 保留帧指针(便于 perf 分析)
gcc -fno-omit-frame-pointer -O2 -o hello main.c

4.10 架构相关选项

# 指定目标 CPU
gcc -march=native -o hello main.c          # 本机 CPU(自动检测最优指令集)
gcc -march=x86-64-v3 -o hello main.c      # x86-64-v3 微架构级别

# 调优目标
gcc -march=skylake -mtune=skylake -o hello main.c

# 指令集扩展
gcc -msse4.2 -o hello main.c               # 启用 SSE 4.2
gcc -mavx2 -o hello main.c                 # 启用 AVX2
gcc -mavx512f -o hello main.c              # 启用 AVX-512

# ARM 架构
aarch64-linux-gnu-gcc -march=armv8-a -o hello main.c
arm-linux-gnueabihf-gcc -march=armv7-a -mfpu=neon -o hello main.c

x86-64 微架构级别

级别特性要求典型 CPU
x86-64基线(SSE2)所有 x86-64 CPU
x86-64-v2SSE4.2, POPCNT, …Intel Nehalem+, AMD Bulldozer+
x86-64-v3AVX2, BMI2, …Intel Haswell+, AMD Excavator+
x86-64-v4AVX-512Intel Skylake-X+, AMD Zen 4+

4.11 常用编译选项速查表

开发阶段推荐选项

# 日常开发(调试友好 + 基本警告)
gcc -Wall -Wextra -g -Og -std=c17 -o hello main.c

# CI/CD(严格警告 + 所有警告视为错误)
gcc -Wall -Wextra -Wpedantic -Werror -std=c17 -o hello main.c

# 生产构建(优化 + 警告 + 安全加固)
gcc -Wall -Wextra -O2 -std=c17 \
    -fstack-protector-strong \
    -D_FORTIFY_SOURCE=2 \
    -Wl,-z,relro,-z,now \
    -o hello main.c

# 调试构建(调试信息 + 不优化)
gcc -Wall -g3 -O0 -fsanitize=address -o hello_debug main.c

# 性能分析
gcc -Wall -O2 -g -fno-omit-frame-pointer -pg -o hello_profile main.c

# 嵌入式/大小优化
gcc -Wall -Os -ffunction-sections -fdata-sections \
    -Wl,--gc-sections -o hello_small main.c

Makefile 中的典型变量设置

CC = gcc
CFLAGS = -Wall -Wextra -std=c17
CFLAGS_DEBUG = -g3 -O0 -fsanitize=address
CFLAGS_RELEASE = -O2 -DNDEBUG
CFLAGS_CI = -Wall -Wextra -Wpedantic -Werror

LDFLAGS =
LDLIBS = -lm -lpthread

# 根据构建类型选择标志
ifdef DEBUG
    CFLAGS += $(CFLAGS_DEBUG)
else
    CFLAGS += $(CFLAGS_RELEASE)
endif

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

hello: main.o greet.o
	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)

要点回顾

要点核心内容
优化级别-O0(调试) / -Og(开发) / -O2(生产) / -O3(性能)
警告-Wall -Wextra 是最低要求,CI 中加 -Werror
标准C: -std=c17,C++: -std=c++17
头文件-I 指定搜索路径,注意 ""<> 的搜索顺序差异
库链接-L 指定路径,-l 指定库名,注意链接顺序
宏定义-D 定义,-U 取消
架构-march=native 针对本机优化,-march=x86-64-v2 保证兼容性

注意事项

不要使用 -w: -w 禁用所有警告,这会隐藏潜在问题。始终使用 -Wall -Wextra,对特定警告使用 -Wno-<name> 精确抑制。

-O2 不等于 -O1 + -O1: 每个优化级别是独立的,不存在叠加关系。

库链接顺序: 被依赖的库放在后面。如果遇到 undefined reference 错误,先检查链接顺序。

-march=native 不可移植: 使用 -march=native 编译的程序可能无法在不同 CPU 上运行。


扩展阅读


下一步

05 - 预处理器详解:深入理解 GCC 预处理器的工作原理、宏定义技巧和条件编译。