news 2026/10/12 4:53:19

编译四大阶段

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
编译四大阶段

一.预编译阶段(预处理,gcc -E main.cpp main.i)

输入:.cpp源码;

输出:.i预处理后的文本文件,仍然是C/C++源码文本,不是机器码。

1.#define宏展开,删除#define定义

例子:#define PI 3.14,代码里所有PI全部替换成3.14;宏请按不原地展开,之后删掉#define这一行。

注意:只是纯文本替换,不做语法检查!写错宏,预处理阶段不会报错,等到后面编译阶段才报错。

2.处理条件预编译 #if / #ifdef / #ifndef / #endif

根据条件判断,直接删掉不满足条件的代码行。

例:#ifdef WIN32 如果不是Windows平台,这一段代码直接被删掉,不会参与后续编译。

3.处理#include头文件

把#include "xxx.h"对应的整个头文件内容,原封不动复制粘贴到当前代码这个位置。

头文件会递归处理:头文件里面还包含别的头文件,继续复制。

4.删除所有注释

//单行注释和/*块注释*/全部删掉,替换成空格。注释对程序没有任何作用,预处理直接清理掉。

5.添加行号,文件名标识

就是# 行号 “文件名”标记。后面编译报错,gdb调试的时候,才能告诉你main.cpp第25行出错。如果没有这个标记,编译器分不清代码来自哪个文件哪一行。

6.保留#pragma指令

#pragma是留给特定编译器的指令,不是标准C语法。预处理不会删掉,交给后面编译器处理。

例:#pragma once防止头文件重复包含。

预处理总结:纯文本操作,不做语法检查,只是增删替换。

二.编译阶段(gcc -S main.i -o main.s)

输入:预处理后的.i文件;输出:.s汇编代码文件(文本)

一共5步:词法分析->语法分析->语义分析->代码优化->汇总符号

1.词法分析

把连续代码切成一个个[单词token],把代码字符串切割:关键字if,变量名a,运算符+,数字10。

比如:int a = 1+2;拆成int a = 1 + 2 ;

识别非法字符,比如中文标点,这里就报错。

2.语法分析

按照C语法,生成语法树(抽象语法树AST),检查句子语法是否合法。

比如:int = 10; 变量名确实,语法错误,直接报编译错误。

3.语义分析

检查类型是否合法(重点!)

语法没问题,但类型不对在这里报错。

例如:int a = "abc"; 把字符串赋值给int,语法没问题,语义错误。

做类型检查,类型转换。

4.代码优化

对AST做优化,简化代码。

比如:int a = 1+2; 直接优化成a=3,不用运行时再计算。

5.汇总符号

收集所有符号,建立符号表。

符号:变量名,函数名。记录函数,变量在那定义,类型是什么。

编译阶段产出:汇编代码.s,是人类刻度的汇编指令文本。

三.汇编阶段(gcc -c main.s -o main.o)

输入:.s汇编代码;输出:目标文件.o(Windows叫.obj),二进制文件。

汇编器as,把汇编指令翻译成CPU能识别的二进制机器码。

1.将汇编指令翻译成二进制,划分各个section(段)

.test段:存放代码指令(函数二进制机器码);

.data段:已经初始化的全局变量;

.bss段:未初始化全局变量,不占用目标文件空间,只记录大小。

2.生成符号表

记录本.o里面的函数,变量符号。

***重点:此时只是单个目标文件,符号没有分配最终虚拟地址!!!

如果调用别的文件的函数,汇编阶段只标记[这个符号待解析],不知道它的真实地址。

举例:main.o调用func(),func在另一个test.o里面。汇编阶段不知道func在哪,先记下来,留给链接阶段处理。

四.链接阶段(link,gcc把多个.o合并成可执行文件)

输入:多个.o目标文件+静态库.a;输出:可执行文件a.out/main.exe

两大核心步骤:

1.合并段,合并符号表,符号解析,分配虚拟地址

(1)把所有.o里面同名section合并:所有.text合并成一个大代码段;所有.data合并成数据段。

(2)合并所有目标文件的符号表。

(3)符号解析:找到每一个符号唯一的定义。

比如:main.o引用func,链接器去其他.o里面找func的定义,找到func对应的符号。

(4)给每一个符号分配最终的虚拟地址。

2.符号重定位(最核心)

重定位:把代码里所有引用符号的地方,把原来临时占位的地址,替换成刚刚分配好的虚拟地址。

举个例子:

main函数调用func(),在main.o里面call指令的地址是占位符。

链接找到func的虚拟地址,修改call指令,填入func真正的虚拟地址。

如果链接找不到符号定义:报未定义引用undefined reference,这是链接期错误,不是编译错误!

链接的两种类型:

1.静态链接

多个目标文件.o打包归档在一起的文件,不是可执行程序。

链接阶段行为:链接器扫描你的.o目标文件,发现你调用了库里的函数,直接把被调用函数对应的那一段机器码,从静态库里面拷贝出来,合并到最终的可执行文件中。

特点:

(1)运行时不再依赖原静态库文件,库代码已经完整复制exe/a.out,拿到这个可执行文件就能直接跑,移植方便。

(2)可执行文件体积更大,因为包含了库的机器码。

(3)缺点:多个程序如果都用同一个静态库,每个程序都会单独复制一份库代码,磁盘上存在多份重复代码,浪费磁盘空间。

(4)库升级:如果静态库升级,必须重新编译链接整个项目,重新生成可执行程序,否则程序依旧使用旧版本库代码。

总结:静态链接在编译链接期,把所需要的库代码复制进可执行文件,运行不依赖库,程序体积大,多程序会重复保存库代码。

2.动态链接

动态库后缀:Linux .so,Windows .dll

链接阶段行为:

不会把库的机器码复制进可执行文件。链接器只做两件事:

1.检查你调用的函数在动态库里面是否存在,完成符号校验;

2.在可执行文件里,仅仅记录依赖哪个动态库,用到哪些函数,只留下一张“函数索引表”。

程序运行时行为:

操作系统加载可执行文件到内存之后,再根据记录的依赖信息,去磁盘找到对应的.so/.dll动态库,把动态库加载进内存,然后完成符号重定位,把函数调用绑定到动态库内函数的真实地址。

特点:

(1)磁盘上只保存一份动态库,多个应用程序可以共享内存里这一份动态库代码,节省磁盘和物理内存。

(2)可执行文件本身体积更小。

(3)库升级:替换新版本的.so/.dll文件即可,不需要重新编译链接你的程序,下次运行程序自动加载新版本库。

(4)缺点:运行环境必须存在对应的动态库文件,缺少库直接运行失败(经典报错:xxx.so not found),移植时需要连带部署动态库。

总结:动态链接编译时只记录库依赖,不复制代码;程序运行时操作系统才加载动态库,多程序共享同一份库,程序体积小,但运行环境必须有对应动态库。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 4:52:17

AI日报制作全流程:从信息采集到知识体系构建的实操方法论

1. 一份AI日报的诞生逻辑每天早上八点前,我会把一份大约三千字的AI日报推送到几个内部群。这个习惯从2024年延续到现在,中间迭代过至少五个版本。很多人以为做日报就是"把新闻复制粘贴一下",但真正做过的人知道,一份能让…

作者头像 李华
网站建设 2026/10/12 4:52:08

UE实战进阶:架构设计、性能调优与网络同步核心指南

1. 从“能跑”到“跑得好”:UE实战到底在解决什么问题很多人学UE,前期都卡在“能跑起来”这个阶段——蓝图连上了,角色能动了,UI弹出来了,就觉得差不多了。但真正进入项目实战,你会发现“能跑”和“跑得好”…

作者头像 李华
网站建设 2026/10/12 4:51:04

小白程序员必看:LLM推理延迟的“快慢”真相与优化秘籍

本文深入剖析了LLM推理延迟的复杂性,指出其包含Prefill和Decode两个阶段,分别受GPU算力和显存带宽限制。Prefill阶段决定首token生成速度(TTFT),主要瓶颈是计算量;Decode阶段决定后续token生成间隔&#xf…

作者头像 李华
网站建设 2026/10/12 4:51:04

AI时代,这10类工作将越来越香,普通人如何避开内卷安稳立足?

文章指出,AI正逐渐取代简单重复性工作,如文案、数据录入、客服等,而需要人际交往、复杂问题解决、情感关怀和创新能力的工作将更加有价值。普通人应关注能“搞定人”的销售、能解决复杂问题的师傅、能照顾人情绪的服务者、能带团队的管理者以…

作者头像 李华
网站建设 2026/10/12 4:50:20

JavaWeb图书管理系统源码解析:Servlet+JSP+MySQL实战教程

简介:这是一份基于JavaWeb的图书馆管理系统完整项目源码,适合正在做课程设计、毕业设计或想系统梳理服务端开发流程的学习者。项目以MVC分层为核心,后端使用Servlet处理业务逻辑,JSP完成页面展示,通过JDBC操作MySQL数据…

作者头像 李华
网站建设 2026/10/12 4:50:18

PS5桌面坞站搭建指南:HDMI切换、扩展坞与理线方案全解析

很多PS5玩家都有过这样的经历:主机到手当天兴冲冲地接上显示器和音箱,结果画面是出来了,HDMI线却把电视柜缠得乱七八糟;玩到一半手柄没电,想插一根线接着充,又找不到合适的USB口;要截图或者录素…

作者头像 李华