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 并配置多版本共存。