GCC/G++ 编译器完全指南:从编译流程到进阶用法(附实操案例)


🔥草莓熊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++,核心用法完全一致。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
zandy10112 小时前
当BI遇见AI Agent:衡石科技如何重塑企业数据分析工作流
人工智能·科技·数据分析·ai agent·data agent
草莓熊Lotso2 小时前
C++11 核心特性实战:列表初始化 + 右值引用与移动语义(附完整代码)
java·服务器·开发语言·汇编·c++·人工智能·经验分享
Bigan(安)3 小时前
【奶茶Beta专项】【LVGL9.4源码分析】09-core-global全局核心管理
linux·c语言·mcu·arm·unix
初夏睡觉3 小时前
从0开始c++,但是重置版,第1篇(c++基本框架)
开发语言·c++
渡我白衣3 小时前
AI应用层革命(七)——智能体的终极形态:认知循环体的诞生
人工智能·深度学习·神经网络·目标检测·microsoft·机器学习·自然语言处理
老王熬夜敲代码3 小时前
进程PCB
linux·笔记
qq_281317474 小时前
链路穿透-运维
网络
Wnq100728 小时前
世界模型 AI:认知跃迁的可行性与本质性挑战
人工智能
穷人小水滴8 小时前
科幻 「备用肉身虫」 系列设定集 (AI 摘要)
人工智能·aigc·科幻·未来·小说·设定