基本概念
gcc:GNU 编译器套件,支持 C、C++ 等多语言,是 Linux 开发的核心工具。
g++:GCC 的 C++ 专用编译器,默认处理
.cpp文件,自动启用 C++ 特性(如名称修饰、异常处理)。推荐专门使用gcc编译c语言,g++编译c++
| C | gcc | .c | gcc 文件名.c |
| C++ | g++ | .cpp | g++ 文件名.cpp |
执行流程
编译后默认生成名为a.out的可执行文件;
在 Linux 终端中通过./a.out执行程序。

可以看到会在当前路径下生成一个。/a.out的可执行文件
gcc/g++编译器的执行步骤
gcc/g++编译器如何将我们的源文件生成可执行程序?
进行了如下四个步骤 预处理(预编译),编译,汇编,链接
下面我们分别演示一下吧
补充知识:
跨平台开发的底层支持机制
1. 语言本身的 “平台无关性”
C/C++ 等语言的语法、标准库(如 C 的
<stdio.h>、C++ 的<iostream>)是由国际标准(如 C11、C++20)统一定义的,与操作系统无关。开发者只需遵循标准语法编写代码,无需针对不同系统修改核心逻辑。2. 编译器的 “平台适配能力”
Windows:有 MSVC(Visual Studio 编译器)、MinGW(GCC 的 Windows 移植版)等工具,可将 C/C++ 代码编译为 Windows 兼容的可执行文件(
.exe)。Linux:通过 GCC/G++ 将代码编译为 Linux 下的可执行文件(无后缀)。编译器的核心作用是 “将统一的代码转换为对应系统的机器指令”,从而实现同一代码在不同系统的编译适配。
3. 操作系统提供的 “开发环境支持”
两类系统都提供了文件系统、内存管理、进程调度等基础功能,可支撑程序的运行。
系统还提供了系统调用接口(如 Linux 的
write、Windows 的WriteFile),编译器 / 标准库会封装这些接口,让开发者通过统一的标准库函数(如printf)调用不同系统的底层功能。4. 开发工具链的 “跨平台生态”
除编译器外,Windows/Linux 都具备完整的开发工具链:
编辑器 / IDE:如 VS Code、CLion 等可在双系统运行;
构建工具:Make、CMake 等可跨平台管理编译流程;
调试工具:GDB(Linux)、WinDbg(Windows)等适配对应系统的程序调试。
1.预处理
gcc -E main.c -o main.i给预处理阶段的输出指定文件 main.i预处理
1.文本替换与宏处理
宏定义展开:处理
#define定义的宏,将代码中所有宏名替换为对应内容(如#define PI 3.14会把代码中PI替换为3.14)。条件编译:通过
#if/#ifdef/#elif/#else/#endif控制代码的编译范围(例如仅在调试模式下保留日志代码)。2. 头文件包含
处理
#include指令,将指定头文件的内容直接插入当前代码中(如#include <stdio.h>会把标准输入输出库的声明嵌入源代码)。3. 代码清理
删除注释(将
//或/*...*/包裹的内容全部移除);处理空白字符(合并多余换行、空格,统一代码格式)。
4. 生成预处理文件
输出
.i(C)或.ii(C++)格式的预处理文件,包含展开后的完整代码(可通过gcc -E main.c -o main.i命令查看)。


我们可以看到代码变成了700多行然后因为没有定义宏HELLO所以条件编译执行了else
另外也可以在外部定义宏
gcc -E 文件名 -o 预处理后的文件名 -D 宏名
当我定义了宏HELLO并且值为真时就会运行if


可以看到只保留了if条件下的
2. 编译
g++ -S main.cpp -o main.s给编译阶段的输出指定汇编文件 main.s 编译
1.“翻译” 源代码:把人类能看懂的 C/C++ 代码(比如
int main() { printf("hello"); }),转换成计算机能理解的 “汇编语言”(底层指令集,比如movl $1, %edi这类指令)。2.语法 + 语义检查:这是编译阶段的关键作用!如果代码有语法错误(比如少写分号、括号不匹配)、语义错误(比如变量未定义、函数调用参数不匹配),编译器会直接报错,阻止后续流程。
示例错误:
int a = ;(语法错误)、printf("hello")(少写括号,语法错误)、int b = "abc";(类型不匹配,语义错误)。3.初步优化(可选):如果加了优化参数(比如
-O2),编译阶段会对代码进行逻辑优化(比如删除无用代码、合并重复计算),让后续生成的程序更快、体积更小。
这就是汇编代码

3. 汇编
gcc -c main.s -o main.o给汇编阶段的输出指定目标文件 main.o 汇编 ![]()
核心任务:将「编译阶段生成的汇编代码(.s 文件)」翻译成 CPU 可直接识别的「二进制机器指令」,最终生成「目标文件(.o 后缀,又称可重定位目标文件)」;
关键附加操作:生成「符号表」,记录代码中变量、函数的地址信息,为后续链接阶段解决跨文件依赖(如调用其他 .o 文件的函数)提供依据。
输出文件(.o)的特性:是二进制文件(含机器指令),但不具备可执行能力—— 缺少操作系统可识别的执行结构、未解决外部依赖(如标准库函数
printf),即使添加执行权限也无法直接运行;与其他阶段的关联:汇编是 “翻译汇编代码→机器码” 的纯转换过程,不做语法检查(语法检查在编译阶段完成),也不处理依赖(依赖处理在链接阶段完成);
核心价值:将底层的汇编指令转为计算机能直接执行的机器码,是 “高级语言→可执行程序” 中 “底层指令落地” 的关键步骤,后续需通过链接将 .o 文件转为可执行程序。

vim查看汇编后的二进制文件发现已经是乱码了
od 汇编后的文件名,借助工具 od 来进行打开二进制文件进行查看

4. 链接
gcc main.o -o main将目标文件 main.o链接为可执行文件main链接
合并文件:将多个目标文件(如
main.o、func.o)及所需的库文件(如libc.so)整合为一个完整文件;解决符号引用:找到目标文件中未定义的函数 / 变量(如调用的
printf、自定义函数),关联到库文件或其他目标文件中的对应实现;生成可执行格式:按照操作系统要求的格式(如 Linux 的 ELF、Windows 的 PE)组织文件,使系统能加载并运行。
库
那刚才已经提到了链接会链接对应的库文件,那什么是库,库又有什么?
什么是 “库”
库是封装了通用功能的代码集合,是软件开发中预实现的工具包,供开发者直接调用以完成特定任务,无需重复编写基础逻辑。
库里包含的内容
库的核心组成是可复用的代码模块,具体形式根据库的类型和用途不同而变化,常见内容包括:
函数 / 方法:实现具体功能的代码单元(如字符串拼接函数、文件读写方法);
数据结构:预定义的容器(如 C++ STL 中的
vector、map);类 / 对象:面向对象语言中封装了属性与行为的代码单元(如图形库中的
Window类);常量 / 宏定义:通用的配置值或编译指令(如数学库中的
PI常量);接口定义:约定调用方式的规范(如网络库中的通信接口)。
这些内容被打包成特定格式的文件(如静态库
.a/.lib、动态库.so/.dll),供编译器或程序在构建、运行时使用。所以咱们平时调用的一些库函数就是从库里来的,头文件则是这些库函数的声明
一、库的命名规则
库名的格式为
lib[name].so.xxx(动态库)或lib[name].a.xxx:
[name]是库的核心名称(例如 C 标准库的name为c,对应库文件为libc.so/libc.a);
xxx代表库的版本号,用于区分不同版本的库。二、不同系统的库格式
系统
动态库格式
静态库格式
Linux
.so
.aWindows
.dll
.lib三、系统默认库的配置特点
多数系统默认仅预装动态库(例如 Linux 中
/usr/lib64/libc.so是默认的 C 语言动态库);静态库(如
/usr/lib64/libc.a)通常不会默认安装,需手动执行安装命令(如之前提到的yum install glibc-static)。这一设计的原因是:动态库可被多个程序共享,能节省系统存储空间;而静态库因会增大程序体积,仅在需要独立部署程序的场景下才需安装。
系统一般会默认提供动态库,静态库需要自己去安装


![]()
我们的.o文件和库是如何进行链接的
- 程序库分为动态库(共享库)、静态库,对应动态链接、静态链接两种方式。
- 动态链接:程序运行时加载动态库,运行依赖动态库,库缺失会导致程序(或系统)无法运行。
- 静态链接:编译时仅将静态库中被使用的函数复制到目标程序,程序运行不依赖静态库。
- Linux 编译可执行程序时,默认采用动态链接(链接动态库)。
- 特性差异:动态链接程序体积小但依赖外部库;静态链接程序体积大但可独立运行。
ldd 可执行程序 可以查看程序运行需要的共享库

系统默认未预装 C/C++ 静态库,因此需要手动安装。
sudo apt install -y libc6-dev
安装c静态库
要生成静态链接的可执行程序,需在编译命令中添加 -static选项 ,该选项会指定编译器使用静态库进行链接,最终生成不依赖外部动态库的可执行程序。
![]()
![]()
可以看到使用静态库明显大于使用动态库的空间
静态链接方式由于是将库进行拷贝,所以占用空间明显大于动态链接(程序执行时由运行的链接文件进行加载库)方式
静态链接的前提
使用
-static选项进行静态链接的必要条件是系统中已安装对应的静态库,若无静态库则无法执行静态链接。2. GCC 的链接方式优先级
GCC 的链接策略遵循以下顺序:
优先使用动态库(若系统中存在对应动态库);
若动态库不存在,再尝试使用静态库(若系统中存在对应静态库);
若动态库、静态库均不存在,则编译报错。
3. 可执行程序的链接方式特点
实际生成的可执行程序,通常并非 “纯动态链接”,而是动态链接与静态链接混合的方式(部分依赖动态库、部分嵌入静态库代码)。
file 可执行程序,可以查看我们的可执行程序的链接方式





4779


被折叠的 条评论
为什么被折叠?



