GCC 完全指南 / 01 - GCC 简介与历史
01 - GCC 简介与历史
了解 GCC 的诞生背景、发展历程、支持的编程语言以及编译器内部架构。
1.1 什么是 GCC
GCC(GNU Compiler Collection,GNU 编译器套件)是 GNU 项目的核心组成部分,也是开源世界中最重要的基础设施之一。它不仅是一个 C 编译器,而是一套支持多种编程语言的编译器集合。
┌─────────────────────────────────────────────────────┐
│ GCC (GNU Compiler Collection) │
│ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌──────┐ │
│ │ gcc │ │ g++ │ │ gfortran│ │ gcj │ │ gccgo│ │
│ │ C │ │ C++ │ │Fortran│ │ Java │ │ Go │ │
│ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └──┬───┘ │
│ └─────────┴─────────┴─────────┴─────────┘ │
│ 共享编译后端 │
│ (优化器 · 汇编生成 · 链接) │
└─────────────────────────────────────────────────────┘
GCC 的核心价值
| 特性 |
说明 |
| 自由软件 |
基于 GPL 许可证,任何人都可以自由使用、修改和分发 |
| 多语言支持 |
支持 C、C++、Fortran、Go、D、Ada 等十余种语言 |
| 跨平台 |
可运行于 Linux、macOS、Windows(MinGW/WSL)等 |
| 高度可移植 |
支持 x86、ARM、RISC-V、MIPS、PowerPC 等数十种架构 |
| 成熟稳定 |
30+ 年历史,经过无数项目的验证 |
| 丰富扩展 |
内置大量扩展属性、内置函数和优化技术 |
1.2 GCC 的历史
诞生与早期发展
GCC 由 Richard Stallman 于 1987 年发布第一个版本,是 GNU 项目的关键组件。GNU 项目的目标是创建一个完全自由的类 Unix 操作系统。
| 年份 |
版本 |
里程碑事件 |
| 1984 |
— |
GNU 项目启动,Richard Stallman 发起自由软件运动 |
| 1985 |
— |
发布 GNU Manifesto(GNU 宣言) |
| 1987 |
GCC 1.0 |
第一个版本发布,仅支持 C 语言,只能在 Sun 和 VAX 上运行 |
| 1988 |
— |
GCC 被移植到更多平台,获得更广泛的使用 |
| 1992 |
GCC 2.0 |
引入 C++ 支持(g++),重构编译器框架 |
| 1997 |
— |
EGCS(Experimental/Enhanced GCC)项目启动,引入社区化开发模式 |
| 1999 |
GCC 2.95 |
EGCS 与 GCC 主线合并,编译质量和优化能力大幅提升 |
| 2001 |
GCC 3.0 |
重大架构重构,引入 GIMPLE 中间表示,支持 Fortran 和 Java |
| 2004 |
GCC 4.0 |
全新的 Tree-SSA 优化框架,显著提升优化能力 |
| 2012 |
GCC 4.7 |
实验性 C++11 支持 |
| 2014 |
GCC 4.9 |
引入 AddressSanitizer、UBSan |
| 2015 |
GCC 5.0 |
默认标准切换为 C++14,OpenMP 4.0 支持 |
| 2017 |
GCC 7 |
默认标准切换为 C++17,-Og 优化改进 |
| 2019 |
GCC 9 |
C++20 部分支持,-fanalyzer 静态分析器 |
| 2021 |
GCC 11 |
C++20 完整支持,DWARF 5 默认启用 |
| 2022 |
GCC 12 |
-O3 改进,C++23 实验性支持,改进的 sanitizer |
| 2023 |
GCC 13 |
C++23 进一步支持,Ada 2022 支持 |
| 2024 |
GCC 14 |
改进的 C++23/C23 支持,新的优化选项 |
GCC 与 EGCS 的历史
1997 年,GCC 的开发速度放缓,社区开发者启动了 EGCS(Experimental/Enhanced GNU Compiler System) 分支。EGCS 拥有更开放的开发模式和更快速的迭代。1999 年,GCC 官方正式采纳 EGCS 的开发模式,两个项目合并。这一事件深刻影响了 GCC 后来的发展——社区驱动的开放开发模式。
GCC 与 LLVM/Clang 的竞争
| 对比维度 |
GCC |
LLVM/Clang |
| 起源 |
1987,GNU 项目 |
2003,UIUC 研究项目 |
| 许可证 |
GPLv3 |
Apache 2.0 + LLVM Exception |
| 架构 |
单体式 |
模块化(库设计) |
| 诊断信息 |
良好 |
更友好,彩色输出、Fix-it Hints |
| 编译速度 |
中等 |
较快 |
| 代码优化 |
极强(尤其数值计算) |
强,持续追赶 |
| 平台支持 |
更广泛(含稀有架构) |
主流平台(x86、ARM、RISC-V) |
| IDE 集成 |
较少 |
更好(libclang) |
| 生态 |
Linux 内核标配 |
macOS/Android/iOS 默认 |
两者互补而非替代的关系:
- Linux 内核: GCC 是唯一官方支持的编译器(Clang 为实验性)
- macOS/iOS: Clang 是默认编译器
- Android NDK: 同时支持 GCC(已弃用)和 Clang
- 嵌入式/硬件厂商: 多数仍提供 GCC 工具链
1.3 GCC 支持的编程语言
GCC 通过前端(Front End)插件化的方式支持多种语言:
| 语言 |
前端程序 |
说明 |
| C |
gcc / cc |
C89/C90、C99、C11、C17、C23 |
| C++ |
g++ |
C++98、C++11、C++14、C++17、C++20、C++23 |
| Fortran |
gfortran |
Fortran 77/90/95/2003/2008/2018 |
| Go |
gccgo |
Go 语言的 GCC 前端 |
| D |
gdc |
D 语言的 GCC 前端 |
| Ada |
gnat |
Ada 语言的 GCC 前端(GNAT) |
| Modula-2 |
gm2 |
Modula-2 语言(GCC 12 起正式支持) |
| Rust |
gccrs |
Rust 语言前端(实验性,GCC 13+) |
| Objective-C/C++ |
gcc / g++ |
苹果 Objective-C 方言(需 GNUstep 运行时) |
各语言标准支持一览
C 语言标准支持
| 标准 |
GCC 选项 |
状态 |
| C89/C90 |
-std=c89 或 -ansi |
完全支持 |
| C99 |
-std=c99 |
完全支持 |
| C11 |
-std=c11 |
完全支持 |
| C17 |
-std=c17 |
完全支持 |
| C23 |
-std=c23 |
GCC 13+ 基本支持 |
C++ 标准支持
| 标准 |
GCC 选项 |
状态 |
| C++98 |
-std=c++98 |
完全支持 |
| C++11 |
-std=c++11 |
完全支持 |
| C++14 |
-std=c++14 |
完全支持(GCC 5+ 默认) |
| C++17 |
-std=c++17 |
完全支持(GCC 8+) |
| C++20 |
-std=c++20 |
完全支持(GCC 11+) |
| C++23 |
-std=c++23 |
大部分支持(GCC 13+) |
1.4 编译器架构
GCC 采用经典的 三阶段编译器架构,前端、中间件、后端分离设计:
┌──────────────────────────────────────────────────────────────────┐
│ GCC 编译器整体架构 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ C 前端 │ │ C++ 前端 │ │Fortran前端│ │ Go 前端 │ │
│ │ (cc1) │ │ (cc1plus)│ │ (f951) │ │ (go1) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └──────────────┴──────────────┴──────────────┘ │
│ │ │
│ GENERIC/GENERIC │
│ │ │
│ ┌───▼───┐ │
│ │ GIMPLE│ ← 中间表示(SSA 形式) │
│ └───┬───┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ 优化 Passes │ ← 100+ 个优化通道 │
│ │ Tree-SSA │ │
│ └──────┬──────┘ │
│ │ │
│ ┌───▼───┐ │
│ │ RTL │ ← 寄存器传输语言 │
│ └───┬───┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ RTL 优化 │ │
│ └──────┬──────┘ │
│ │ │
│ ┌───────────┼───────────┐ │
│ │ │ │ │
│ ┌────▼───┐ ┌───▼────┐ ┌───▼────┐ │
│ │x86 后端│ │ARM后端 │ │RISC-V后端│ │
│ └────┬───┘ └───┬────┘ └───┬────┘ │
│ └───────────┴──────────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 汇编输出 │ → .s 文件 │
│ └─────┬─────┘ │
│ ┌─────▼─────┐ │
│ │ 汇编器 │ → .o 文件 │
│ │ (as/GAS) │ │
│ └─────┬─────┘ │
│ ┌─────▼─────┐ │
│ │ 链接器 │ → 可执行文件 │
│ │ (ld) │ │
│ └───────────┘ │
└──────────────────────────────────────────────────────────────────┘
前端(Front End)
前端负责词法分析和语法分析,将源代码转换为编译器内部的树形中间表示(GENERIC)。
| 前端职责 |
说明 |
| 词法分析(Lexing) |
将源代码字符流分解为 Token |
| 语法分析(Parsing) |
将 Token 序列构建为抽象语法树(AST) |
| 语义分析(Semantic) |
类型检查、作用域分析、符号表构建 |
| 生成 GENERIC |
输出与语言无关的树形中间表示 |
源代码 (hello.c)
│
▼
#include 展开 + 宏替换(预处理)
│
▼
Token 序列: int main ( ) { printf ( "hello" ) ; ... }
│
▼
抽象语法树 (AST)
│
▼
GENERIC 树(语言无关)
中端(Middle End)
中端负责语言无关的优化,这是 GCC 优化能力的核心。中间表示经过转换为 GIMPLE(SSA 形式)后,进入一系列优化通道(Pass)。
| 优化 Pass 类别 |
示例 |
| 前端级优化 |
常量折叠、死代码消除 |
| GIMPLE 优化 |
循环优化、内联、向量化、别名分析 |
| SSA 优化 |
公共子表达式消除、值编号、标量替换 |
| 循环优化 |
循环展开、循环交换、循环分块、自动向量化 |
GCC 中包含 100+ 个优化 Pass,主要包括:
- tree-ssa : SSA 基础优化
- tree-vrp : 值范围传播
- tree-ccp : 条件常量传播
- tree-licm : 循环不变量外提
- tree-loop : 循环优化集
- tree-vectorize: 自动向量化
- tree-inline : 函数内联
后端(Back End)
后端负责将通用中间表示转换为目标架构的机器码,包括指令选择、寄存器分配和指令调度。
| 后端职责 |
说明 |
| RTL 生成 |
将 GIMPLE 转换为 RTL(Register Transfer Language) |
| 指令选择 |
将 RTL 操作映射为目标架构指令 |
| 寄存器分配 |
将虚拟寄存器映射为物理寄存器 |
| 指令调度 |
重排指令以充分利用流水线 |
| 代码生成 |
输出汇编代码(AT&T 或 Intel 格式) |
1.5 GCC 的编译产物
GCC 各阶段的可执行程序和文件后缀:
| 程序 |
功能 |
输入/输出 |
cpp |
C 预处理器 |
.c → .i(预处理后源码) |
cc1 |
C 编译器 |
.c → .s(汇编代码) |
cc1plus |
C++ 编译器 |
.cpp → .s |
as |
GNU 汇编器 |
.s → .o(目标文件) |
ld |
GNU 链接器 |
.o → 可执行文件或库 |
collect2 |
链接器前端 |
包装 ld,处理构造/析构函数 |
文件后缀一览
| 后缀 |
文件类型 |
说明 |
.c |
C 源文件 |
|
.C, .cc, .cpp, .cxx |
C++ 源文件 |
|
.h |
头文件 |
C/C++ 共用 |
.hpp, .hxx |
C++ 头文件 |
|
.i |
预处理后的 C 源文件 |
跳过预处理阶段 |
.ii |
预处理后的 C++ 源文件 |
|
.s |
汇编源文件 |
|
.S |
含预处理指令的汇编文件 |
|
.o |
目标文件(Object) |
ELF 格式 |
.a |
静态库(Archive) |
ar 打包的 .o 集合 |
.so |
共享库(Shared Object) |
动态链接库 |
.lo |
共享目标文件 |
PIC 版本的目标文件 |
1.6 GCC 版本命名与获取
版本号格式
GCC 版本号格式: major.minor.patch
示例: GCC 13.2.0
- 13 : 主版本号(大版本,有架构变更)
- 2 : 次版本号(增加功能、修正错误)
- 0 : 补丁版本号(纯错误修复)
查看版本:
gcc --version
gcc -dumpversion
gcc -dumpfullversion
获取 GCC
| 获取方式 |
命令/网址 |
说明 |
| 官网源码 |
https://gcc.gnu.org/ |
可从 SVN 或 Git 获取最新源码 |
| 发行版包管理 |
apt install gcc / dnf install gcc |
简便,但版本可能较旧 |
| Snap |
sudo snap install gcc-13 |
Canonical 维护 |
| 手动编译 |
从源码构建 |
最灵活,可选组件 |
快速查看 GCC 信息
# 查看版本
gcc --version
# 查看编译器的编译参数(构建 GCC 时使用的选项)
gcc -v
# 查看支持的 CPU 类型
gcc --target-help
# 查看所有预定义的宏
gcc -dM -E - < /dev/null
# 列出所有已安装的语言前端
gcc -x c -v -E /dev/null 2>&1 | head -5
1.7 GCC 在开源生态中的地位
依赖 GCC 的关键项目
| 项目 |
说明 |
| Linux 内核 |
唯一官方支持的编译器(Clang 为实验性支持) |
| GNU 工具链 |
glibc、binutils、coreutils 等均依赖 GCC |
| GCC 运行时库 |
libgcc、libstdc++、libgomp 等 |
| Debian/RPM |
系统包默认使用 GCC 编译 |
| HPC(高性能计算) |
Fortran 编译器的事实标准 |
| 嵌入式系统 |
大多数芯片厂商的 SDK 基于 GCC |
GCC 运行时库
| 库 |
说明 |
libgcc |
低级运行时支持库,提供软浮点、整数除法等 |
libstdc++ |
C++ 标准库实现 |
libgomp |
OpenMP 运行时库 |
libgfortran |
Fortran 运行时库 |
libgo |
Go 语言运行时 |
libatomic |
原子操作库 |
libsanitizer |
Sanitizer 运行时库(ASan、TSan、UBSan) |
要点回顾
| 要点 |
核心内容 |
| GCC 定义 |
GNU Compiler Collection,自由软件编译器套件 |
| 历史 |
1987 年由 Richard Stallman 发布,30+ 年历史 |
| 语言支持 |
C、C++、Fortran、Go、D、Ada、Modula-2、Rust(实验性) |
| 架构 |
三阶段:前端 → 中端(GIMPLE/SSA)→ 后端(RTL) |
| 竞争对手 |
LLVM/Clang,两者互补共存 |
| 地位 |
Linux 内核及 GNU 工具链的核心基础设施 |
注意事项
GPLv3 许可证: GCC 采用 GPLv3 许可证,GCC 编译的程序的源代码不受 GPL 传染——只有修改 GCC 本身才需要开源。但 GCC 运行时库有例外条款(Runtime Library Exception)。
版本差异: 不同 GCC 版本对 C/C++ 标准的支持程度不同,开发前请确认目标版本支持的标准特性。
不要混用编译器: 用 GCC 编译的目标文件和用 Clang 编译的目标文件理论上兼容(遵循同一 ABI),但混用不同编译器版本的 C++ 标准库实现可能引发问题。
扩展阅读
下一步
→ 02 - 安装与环境配置:学习如何在不同操作系统上安装 GCC 并配置多版本共存。