
🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!
🎬 博主简介:

文章目录
- 前言:
- [一. GCC 核心认知:编译的四个阶段(ESc-iso速记)](#一. GCC 核心认知:编译的四个阶段(ESc-iso速记))
-
- [1.1 阶段 1:预处理](#1.1 阶段 1:预处理)
- [1.2 阶段 2:编译](#1.2 阶段 2:编译)
- [1.3 阶段 3:汇编(Assembly)](#1.3 阶段 3:汇编(Assembly))
- [1.4 阶段 4:链接(Linking)](#1.4 阶段 4:链接(Linking))
- [1.5 一键编译(合并四阶段)](#1.5 一键编译(合并四阶段))
- [二. GCC核心功能选项 && 条件编译 && 编译器和语言的历史(理解编译的过程)](#二. GCC核心功能选项 && 条件编译 && 编译器和语言的历史(理解编译的过程))
-
- [2.1 核心功能速查表:](#2.1 核心功能速查表:)
- [2.2 条件编译的逻辑和应用场景](#2.2 条件编译的逻辑和应用场景)
- [2.3 编译的过程:编译器和语言的历史](#2.3 编译的过程:编译器和语言的历史)
- [三. 关键原理:静态链接和动态链接](#三. 关键原理:静态链接和动态链接)
-
- [3.1 静态链接(Static Linking)](#3.1 静态链接(Static Linking))
- [3.2 动态链接(Dynamic Linking)](#3.2 动态链接(Dynamic Linking))
- [3.2 查看依赖的库文件](#3.2 查看依赖的库文件)
- [3.3 通过一个故事感性的理解一下库](#3.3 通过一个故事感性的理解一下库)
-
- [3.3.1 阶段一:动态库的故事(共享网吧模式)](#3.3.1 阶段一:动态库的故事(共享网吧模式))
- [3.3.2 阶段二:静态库的故事(自带电脑模式)](#3.3.2 阶段二:静态库的故事(自带电脑模式))
- [3.3.3 核心概念对比 && 整体总结](#3.3.3 核心概念对比 && 整体总结)
- 结尾:
前言:
在 Linux C/C++ 开发中,GCC(GNU Compiler Collection)是无可替代的编译器工具。无论是简单的 Hello World 程序,还是复杂的大型项目,GCC 都能完成从源代码到可执行文件的转换。但很多开发者只停留在
gcc hello.c -o hello的基础用法,对其编译流程、链接原理和进阶选项了解甚少。本文结合核心知识点,从 GCC 的编译四阶段、核心选项、静态 / 动态链接,到优化与调试配置,全方位拆解 GCC 的使用技巧,帮你从 "会用" 升级到 "精通"。
一. GCC 核心认知:编译的四个阶段(ESc-iso速记)
GCC 编译 C/C++ 程序并非一步到位,而是分为预处理、编译、汇编、链接四个阶段,每个阶段完成特定任务,最终生成可执行文件。
1.1 阶段 1:预处理
- 核心任务:宏替换、删除注释、条件编译、头文件展开(不进行语法检查);
- 关键选项 :
-E(仅执行预处理,停止后续编译); - 输出文件 :
.i后缀(预处理后的 C 文件); - 实操命令:
bash
[Lotso@VM-4-4-centos lesson9]$ vim hello.c
[Lotso@VM-4-4-centos lesson9]$ gcc -E hello.c -o hello.i
[Lotso@VM-4-4-centos lesson9]$ ll
total 24
-rw-rw-r-- 1 Lotso Lotso 382 Nov 13 16:50 hello.c
-rw-rw-r-- 1 Lotso Lotso 17170 Nov 13 16:50 hello.i

- 示例:#include <stdio.h>会被展开为 stdio.h 的全部内容,#define MAX 10会替换所有MAX为 10。

1.2 阶段 2:编译
- 核心任务 :检查语法错误,将预处理后的代码(
.i)转换为汇编语言代码; - 关键选项 :
-S(仅执行编译,停止后续流程); - 输出文件 :
.s后缀(汇编文件); - 实操命令:
bash
[Lotso@VM-4-4-centos lesson9]$ gcc -S hello.c -o hello.s
[Lotso@VM-4-4-centos lesson9]$ ll
total 28
-rw-rw-r-- 1 Lotso Lotso 327 Nov 13 16:54 hello.c
-rw-rw-r-- 1 Lotso Lotso 17104 Nov 13 16:54 hello.i
-rw-rw-r-- 1 Lotso Lotso 1094 Nov 13 17:01 hello.s

- 说明 :生成的汇编代码与 CPU 架构相关(如 x86_64、ARM),可直接查看指令逻辑。

1.3 阶段 3:汇编(Assembly)
- 核心任务 :将汇编代码(
.s)转换为机器可识别的二进制目标文件; - 关键选项 :
-c(仅执行汇编,生成目标文件); - 输出文件 :
.o后缀(目标文件,二进制格式,不可执行); - 实操命令:
bash
[Lotso@VM-4-4-centos lesson9]$ gcc -c hello.s -o hello.o
[Lotso@VM-4-4-centos lesson9]$ ll
total 32
-rw-rw-r-- 1 Lotso Lotso 327 Nov 13 16:54 hello.c
-rw-rw-r-- 1 Lotso Lotso 17104 Nov 13 16:54 hello.i
-rw-rw-r-- 1 Lotso Lotso 2184 Nov 13 17:05 hello.o
-rw-rw-r-- 1 Lotso Lotso 1094 Nov 13 17:01 hello.s

bash
[Lotso@VM-4-4-centos lesson9]$ objdump -d hello.o
hello.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: be 0a 00 00 00 mov $0xa,%esi
9: bf 00 00 00 00 mov $0x0,%edi
e: b8 00 00 00 00 mov $0x0,%eax
13: e8 00 00 00 00 callq 18 <main+0x18>
18: be 0a 00 00 00 mov $0xa,%esi
1d: bf 00 00 00 00 mov $0x0,%edi
22: b8 00 00 00 00 mov $0x0,%eax
27: e8 00 00 00 00 callq 2c <main+0x2c>
2c: be 0a 00 00 00 mov $0xa,%esi
31: bf 00 00 00 00 mov $0x0,%edi
36: b8 00 00 00 00 mov $0x0,%eax
3b: e8 00 00 00 00 callq 40 <main+0x40>
40: be 0a 00 00 00 mov $0xa,%esi
45: bf 00 00 00 00 mov $0x0,%edi
4a: b8 00 00 00 00 mov $0x0,%eax
4f: e8 00 00 00 00 callq 54 <main+0x54>
54: be 0a 00 00 00 mov $0xa,%esi
59: bf 00 00 00 00 mov $0x0,%edi
5e: b8 00 00 00 00 mov $0x0,%eax
63: e8 00 00 00 00 callq 68 <main+0x68>
68: be 0a 00 00 00 mov $0xa,%esi
6d: bf 00 00 00 00 mov $0x0,%edi
72: b8 00 00 00 00 mov $0x0,%eax
77: e8 00 00 00 00 callq 7c <main+0x7c>
7c: be 0a 00 00 00 mov $0xa,%esi
81: bf 00 00 00 00 mov $0x0,%edi
86: b8 00 00 00 00 mov $0x0,%eax
8b: e8 00 00 00 00 callq 90 <main+0x90>
90: be 0a 00 00 00 mov $0xa,%esi
95: bf 00 00 00 00 mov $0x0,%edi
9a: b8 00 00 00 00 mov $0x0,%eax
9f: e8 00 00 00 00 callq a4 <main+0xa4>
a4: be 0a 00 00 00 mov $0xa,%esi
a9: bf 00 00 00 00 mov $0x0,%edi
ae: b8 00 00 00 00 mov $0x0,%eax
b3: e8 00 00 00 00 callq b8 <main+0xb8>
b8: be 0a 00 00 00 mov $0xa,%esi
bd: bf 00 00 00 00 mov $0x0,%edi
c2: b8 00 00 00 00 mov $0x0,%eax
c7: e8 00 00 00 00 callq cc <main+0xcc>
cc: be 0a 00 00 00 mov $0xa,%esi
d1: bf 00 00 00 00 mov $0x0,%edi
d6: b8 00 00 00 00 mov $0x0,%eax
db: e8 00 00 00 00 callq e0 <main+0xe0>
e0: b8 00 00 00 00 mov $0x0,%eax
e5: 5d pop %rbp
e6: c3 retq
- 说明:目标文件包含机器指令,但缺少库依赖(如 printf 函数的实现),无法直接运行。
bash
[Lotso@VM-4-4-centos lesson9]$ ./hello.o
-bash: ./hello.o: Permission denied
1.4 阶段 4:链接(Linking)
核心任务 :将目标文件(.o)与系统库、第三方库链接,生成可执行文件;
核心逻辑 :解析未定义符号(如 printf),从库文件(如 libc.so)中找到实现并关联;
输出文件 :默认名为a.out(可通过-o指定名称);
实操命令:
bash
# 也可以 gcc -o hello.exe hello.o
[Lotso@VM-4-4-centos lesson9]$ gcc hello.o -o hello.exe
[Lotso@VM-4-4-centos lesson9]$ ll
total 44
-rw-rw-r-- 1 Lotso Lotso 327 Nov 13 16:54 hello.c
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 13 17:11 hello.exe
-rw-rw-r-- 1 Lotso Lotso 17104 Nov 13 16:54 hello.i
-rw-rw-r-- 1 Lotso Lotso 2185 Nov 13 17:05 hello.o
-rw-rw-r-- 1 Lotso Lotso 1094 Nov 13 17:01 hello.s
[Lotso@VM-4-4-centos lesson9]$ ./hello.exe
10
10
10
10
10
10
10
10
10
10
10

1.5 一键编译(合并四阶段)
日常开发中无需分步执行,GCC 支持一键完成四阶段编译:
bash
# 也可以 gcc -o hello.exe1 hello.c
[Lotso@VM-4-4-centos lesson9]$ gcc hello.c -o hello.exe1
[Lotso@VM-4-4-centos lesson9]$ ll
total 56
-rw-rw-r-- 1 Lotso Lotso 327 Nov 13 16:54 hello.c
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 13 17:11 hello.exe
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 13 17:15 hello.exe1
-rw-rw-r-- 1 Lotso Lotso 17104 Nov 13 16:54 hello.i
-rw-rw-r-- 1 Lotso Lotso 2185 Nov 13 17:05 hello.o
-rw-rw-r-- 1 Lotso Lotso 1094 Nov 13 17:01 hello.s
[Lotso@VM-4-4-centos lesson9]$ ./hello.exe1
10
10
10
10
10
10
10
10
10
10
10

本质是 GCC 自动依次执行 预处理→编译→汇编→链接 ,隐藏了中间文件。

二. GCC核心功能选项 && 条件编译 && 编译器和语言的历史(理解编译的过程)
2.1 核心功能速查表:
| 选项 | 功能描述 | 使用示例 |
|---|---|---|
| -E | 只进行预处理,不编译、汇编和链接 | gcc -E main.c -o main.i |
| -S | 编译到汇编语言,不进行汇编和链接 | gcc -S main.c -o main.s |
| -c | 编译到目标代码,生成.o文件 | gcc -c main.c -o main.o |
| -o | 指定输出文件名 | gcc main.c -o myapp |
| -static | 使用静态链接生成可执行文件 | gcc main.c -static -o app_static |
| -g | 生成调试信息,供GDB使用 | gcc -g main.c -o debug_app |
| -shared | 尽量使用动态库,生成较小的文件 | gcc -shared lib.c -o lib.so |
| -O0 | 不进行优化(编译速度最快) | gcc -O0 main.c -o app |
| -O1 | 基本优化(默认级别) | gcc -O1 main.c -o app |
| -O2 | 较多优化,平衡性能与编译时间 | gcc -O2 main.c -o app |
| -O3 | 最高级别优化(可能增加代码大小) | gcc -O3 main.c -o app |
| -w | 禁止所有警告信息 | gcc -w main.c -o app |
| -Wall | 开启所有常见警告信息 | gcc -Wall main.c -o app |
2.2 条件编译的逻辑和应用场景

2.3 编译的过程:编译器和语言的历史


关键逻辑:
- 为什么任何语言最后都要变成二进制 -> 因为编译器只认识它
- 编译的过程为何是那样的 -> 上面的图中有解释
- 一定是先有语言再有编译器的。并且语言和编译器之间是一个自举的关系
三. 关键原理:静态链接和动态链接
链接阶段的核心是"关联库文件",GCC支持两种链接方式,各有优劣

3.1 静态链接(Static Linking)
- 原理 :编译时将依赖的库文件(如
libc.a)全部复制到可执行文件中; - 关键选项 :
-static; - 实操命令 :
gcc hello.c -o hello_static -static-- 强制静态链接生成可执行文件(因为GCC默认是动态链接的) - 安装命令 :
sudo yum install glibc-static libstdc++-static -y

- 优点:可执行文件不依赖系统库,移植性强。
- 缺点:会让可执行程序变大,运行的时候,加载到内存:占据更多的内存空间,浪费资源!!!
3.2 动态链接(Dynamic Linking)
- 原理 :编译时仅记录库依赖(如
libc.so),程序运行时才加载库文件; - 特点:GCC 默认采用动态链接;
- 实操命令 :
gcc hello.c -o hello_dynamic-- 动态链接(默认) - 优点:文件体积小,库文件被多个程序共享,节省内存和磁盘空间;
- 缺点:依赖系统中对应的库文件,一旦库文件缺失,所有程序都无法运行了。


3.2 查看依赖的库文件
通过 ldd 可以查看可执行程序依赖的动态库
bash
# 动态链接的文件
[Lotso@VM-4-4-centos lesson10]$ ldd hello_dynamic
linux-vdso.so.1 => (0x00007ffe61b15000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2e519cd000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2e51d9b000)
# 静态链接的文件
[Lotso@VM-4-4-centos lesson10]$ ldd hello_static
not a dynamic executable

顺便看看动态链接和静态链接文件的大小对比:

3.3 通过一个故事感性的理解一下库
3.3.1 阶段一:动态库的故事(共享网吧模式)
小王是一中的新生,他学习刻苦,但也需要偶尔上网放松。一中管理严格,但小王从学长张三那里得知,学校东门左转100米再右转100米有一家"小蚂蚁电竞馆"。小王记下地址,在某个周六中午,他按计划完成部分作业后,前往电竞馆上网。老板给他开了编号1234的电脑,小王愉快地度过了中午。下午2:30,他回到学校继续学习。
后来,越来越多的学生知道了电竞馆,纷纷前去放松。直到月考,全班成绩下滑,校长调查后举报导致了电竞馆关闭。
✅️ 阶段一理解:
-
小王(程序)在一中(内存)学习。
-
张三(编译器/链接器)告诉小王电竞馆的地址(动态库的位置)。
-
小王去电竞馆上网(程序运行时链接动态库)。
-
老板给小王开1234号电脑(动态库分配给程序一个函数地址)。
-
电竞馆关闭(动态库缺失)导致所有学生无法上网(程序无法运行)。
声明:这里是为了方便故事举例说明哈,动态库其实也是在内存里的
图示理解 :

bash
┌─────────────┐ 询问地址 ┌─────────────┐
│ 小王 │ ──────────────> │ 张三 │
│ (程序) │ │ (编译器/链接器)│
└─────────────┘ └─────────────┘
│ │
│ 获得地址:"东门左转100m右转100m"
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ 一中 │
│ (内存) │
└─────────────────────────────────────────────┘
│
│ 按计划执行:作业→上网→作业
│
▼
┌─────────────┐ 上网请求 ┌─────────────┐
│ 小王 │ ──────────────> │ 小蚂蚁电竞馆 │
│ (程序) │ │ (动态库) │
└─────────────┘ └─────────────┘
│
│ 分配1234号电脑
│ (库函数地址)
▼
┌─────────┐
│ 愉快上网 │
│ (函数执行)│
└─────────┘
动态库特点:
-
✅ 优点:资源共享,节省内存(多个程序共用同一个库)
-
❌ 缺点:库缺失则所有依赖程序都无法运行
3.3.2 阶段二:静态库的故事(自带电脑模式)
小王因不能上网而学习状态不佳,父亲了解情况后,与校长协商并去小蚂蚁电竞馆找老板买下那台1234号电脑,放在学校供小王使用。此后,小王和同学们都可以在学校内直接上网,无需外出。
✅️ 阶段二理解:
-
小王的父亲和电竞馆老板(静态库的提供者)将电脑(库函数)买下并安置在学校(静态链接)。
-
小王和同学们(多个程序)每人都拥有了一份电脑的拷贝(静态链接时每个程序都包含一份库代码)。
-
现在上网(使用库函数)不依赖校外电竞馆(外部动态库),但每个人都需要占用自己的电脑(内存空间变大)。
图示理解 :

bash
┌─────────────┐ 成绩下滑 ┌─────────────┐
│ 小王 │ ──────────────> │ 爸爸 │
│ (程序) │ │ (静态链接器) │
└─────────────┘ └─────────────┘
│ │
│ 协商购买 │
│ │
▼ ▼
┌─────────────┐ 购买1234电脑 ┌─────────────┐
│ 校长 │ <────────────── │ 电竞馆老板 │
│ (系统环境) │ │ (库提供者) │
└─────────────┘ └─────────────┘
│
│ 电脑安置在学校
│
▼
┌─────────────────────────────────────────────┐
│ 一中 │
│ (内存) │
└─────────────────────────────────────────────┘
│
│ 每个学生都自带电脑
│ (程序各自包含库代码)
▼
┌─────────────┐ 直接使用 ┌─────────────┐
│ 小王和室友 │ ──────────────> │ 个人电脑 │
│ (程序) │ │ (静态库) │
└─────────────┘ └─────────────┘
静态库特点:
-
✅ 优点:程序独立,不依赖外部环境
-
❌ 缺点:每个程序都包含库代码,占用更多存储空间和内存
3.3.3 核心概念对比 && 整体总结
| 特性 | 动态库(小蚂蚁电竞馆) | 静态库(个人电脑) |
|---|---|---|
| 资源使用 | 共享,节省资源 | 独占,浪费资源 |
| 依赖性 | 强依赖,库缺失则全崩 | 独立,自给自足 |
| 更新维护 | 更新库即更新所有程序 | 每个程序需重新编译 |
| 内存占用 | 多个程序共享同一份 | 每个程序都有一份拷贝 |
现实世界类比:
bash
编程世界: 现实世界:
┌─────────────────┐ ┌─────────────────┐
│ 动态库 │ │ 共享网吧 │
│ (共享函数集合) │ │ (共享电脑资源) │
└─────────────────┘ └─────────────────┘
│ │
│ 多个程序共用 │ 多个顾客共用
│ 库缺失=所有程序挂掉 │ 网吧关门=所有人都不能上网
│ │
┌─────────────────┐ ┌─────────────────┐
│ 静态库 │ │ 个人电脑 │
│ (函数嵌入程序内) │ │ (自有电脑资源) │
└─────────────────┘ └─────────────────┘
│ │
│ 每个程序都包含库代码 │ 每个人都有自己的电脑
│ 程序体积大但独立运行 │ 随时可用但占用更多空间
整体总结:
通过这两个故事,我们理解了动态库和静态库的区别:
-
动态库:像是一个共享的电竞馆,多个程序在运行时共享同一个库,节省空间,但一旦库被删除,所有程序都无法运行。
-
静态库:像是每个人都有自己的电脑,程序在编译时就将库代码复制到可执行文件中,因此程序体积大,但可以独立运行,不依赖外部库。
在编程中,我们根据需求选择使用动态库还是静态库。动态库适合多个程序共享,减少内存占用;静态库适合程序独立发布,避免依赖问题。
结尾:
html
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
结语:GCC 的核心价值在于 "灵活与强大"------ 通过不同选项组合,可适配调试、发布、移植等多种场景。掌握编译四阶段让你理解代码转换的本质,静态 / 动态链接的选择影响项目的移植性和性能,而优化与调试选项则能提升开发与运行效率。建议在实际项目中多尝试组合选项(如-Wall -O2 -g),逐步形成适合自己的编译习惯。如果需要编译 C++ 程序,只需将gcc替换为g++,核心用法完全一致。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
