新闻详情

新闻详情

首页 / 资讯中心 / 详情

编译链接的过程发生了什么?

发布时间:2026/9/29 2:19:18来源:尧图网络
编译链接的过程发生了什么?
目录摘要一翻译环境和执行环境二翻译环境工作图三翻译环境工作的展示第一阶段预编译第二阶段编译第三阶段汇编第四阶段链接四链接的功能讲解五模版函数的分离编译摘要文讲解 C 程序的翻译与执行过程包括翻译环境与执行环境、翻译环境工作图以及预编译、编译、汇编、链接四个阶段的具体工作。文章还专门讲解了链接的功能并深入分析了模板函数的分离编译问题——为什么模板声明与定义分离会导致链接错误以及如何解决。一翻译环境和执行环境在ANSI C的任何一种实现中存在两个不同的环境。第1种是翻译环境在这个环境中源代码被转换为可执⾏的机器指令⼆进制指令。第2种是执⾏环境它⽤于实际执⾏代码。也就是说↓1翻译环境代码-二进制指令2执行环境执行二进制指令❓️我们写的代码是文本信息而计算机执行的是二进制的指令这二者之间如何进行的转换换句话说就是翻译环境中发生了什么才让代码-二进制指令如下图解释我们写代码的文件叫作.c源文件会经过编译器生成目标文件不同环境和编译器的目标文件不同比如VS是.obj,Linux环境下的gcc是.o最后通过链接器把生成的目标文件和链接库进行链接生成可执行程序。二翻译环境工作图VS上从写代码到生成可执行程序的过程编译器隐式得为我们做了太多事情才让我们能从.c(源文件)一下到.obj(目标文件)所以为了逐步展示翻译环境中的细节博主选择在Linux环境下的gcc编译器下进行细节上的演示~翻译环境分为四步①预编译也叫作预处理②编译③汇编④链接三翻译环境工作的展示前提准备源文件创建及代码书写我们在gcc中创建一个class110目录在其下创建一个源文件test.c在里面写上如图所示的代码第一阶段预编译首先在终端对我们上面创建的test.c这个源文件进行gcc -E -o test.igcc -E -o test.i的意义执行完预编译阶段就停止然后把内容放在test.i这个文件此时生成了test.i这个文件test.i文件内容如下将test.i和test.c进行对比解释将test.i和test.c进行对比我们就能发现了第一阶段预编译的作用①注释的删除②头文件的包含test.i前面的800行就是#includestdio.h的内容③#define符号的替换直接把M替换成了100④综上所示整个翻译环境是不对.h文件进行处理.h文件是在被包含的源文件进行到预编译这一步时才会去展开总结所有的预处理指令如文件包含、宏定义、条件编译等都是在预编译阶段完成的这些统称为文本操作第二阶段编译解释①首先进行gcc -S test.igcc -S test.i的意义对test.i执行完第二阶段编译就停止内容放进自动生成的test.s中②此时打开test.s发现全是汇编代码 所以编译阶段的工作是将代码翻译成汇编代码放进了自动生成的test.s中第三阶段汇编首先进行gcc -c test.cgcc -c test.c意义为test.c执行完第三阶段汇编就停止放进自动生成的目标文件test.o中然后试图打开test.o被警告因为test.o是二进制文件无法被直接打开所以汇编阶段的功能将汇编指令翻译成了二进制指令强行打开目标文件test.o注ELF是一种文件的格式 需要用指令readelf来读取ELF格式的文件所以我们用指令readelf来读取ELF格式的文件此时发现我们需要输入一些选项-a,-h,-l.......来进行选择性的查看我们使用-a选项即可查看到段表如下解释这就是段表也就是说ELF这种文件格式其实是按照一个一个的段来存储的这也是第四阶段链接中的功能合并段表中所谓的段表也可以-s来查看符号表解释①此时发现符号表中有我们代码的全局变量g_val函数Add函数main库函数printf②这些都被存储到了符号表中 这就是第三阶段汇编的另一个作用形成符号表那么符号表是做什么的在第四阶段里面有作用第四阶段链接如上进行gcc test.o -0 test 即可链接生成可执行程序test然后尝试打开可执行程序test但无法打开因为可执行程序test经过链接之后依旧还是二进制文件所以要想打开用ELF即可四链接的功能讲解假设现在有两个.c源文件解释一个.c只是声明了Add函数一个.c有函数的定义所以链接的功能如下图解释①每个源文件都在编译阶段进行了符号汇总在汇编阶段进行了形成符号表即函数和对应的地址进行合并形成符号表②但是对于test.c中这种仅有声明的函数我们将其地址暂且存为0x0000等到链接的时候与其他的.obj或.0目标文件(.c文件经过预编译编译汇编得到的目标文件)的符号表进行所以就在add.c对应的add.o目标文件中找到了add函数的确切地址完善了add函数对应的符号③Add的地址确认为有效地址add.c中的函数地址就叫作符号表的重定位所以经过链接这一步才能使用函数定义在另一个文件的函数还有其他库中的函数也需要链接这一步才能找到五模版函数的分离编译链接的本质就是通过符号表的合并和符号表的重定义得到各种函数真实的地址从而才能让一个源文件中所涉及到的函数被找到调用❓️但是了解过C模版函数的朋友都知道模版函数是不支持声明(.h)和定义(.cpp)分开文件放的为什么呢解释如下普通函数不管你是否声明定义分离起码最后通过链接就可以得到函数的准确地址但是模版函数即使通过了链接也得不到模版函数的准确地址因为模版函数需要用户给他传递模版参数其才可以实例化出具体的函数才可以生成具体的函数地址模板函数如果没有被实例化编译期就不会生成该模版函数对应的符号于是链接器根本找不到这个符号也就得不到对应的函数地址❓️我们的main中不是在调用模版函数时传递了具体参数吗为什么不会实例化呢前文中的Add.c文件中有着Add函数的定义此时编译器检测到该文件存在非模版函数Add编译器就会无条件为Add函数生成对应符号但是若Add.存放的是模版函数则编译器会无视掉不为其生成对应符号这就是为什么模版函数在经过链接之后仍然找不到函数地址这和你在main中调用模版函数时传递了具体参数无关❓️为什么编译器要这么做呢像对待普通函数一样对待模版函数不行吗因为如果编译器这么做了假设现在有100个文件调用了该模版函数则编译器需要读取100个文件才知道每个文件调用该模板函数时传递的模版类型是什么然后再去Add.c对其中的模板函数分别传递100次类型从而实例化出对应函数生成函数地址太麻烦了影响效率所以编译器不这么做C直接规定模版函数的声明定义不能分离必须写在同一个文件中使用但总有情况使得模版函数不易分离此时还有另一种办法文件 1func.h#pragma once templateclass T T Add(const T a, const T b); // 只有声明文件 2func.cpp#include func.h templateclass T T Add(const T a, const T b) // 定义 { return a b; } // 显式实例化只生成 int 版 template int Addint(const int, const int);文件 3main.cpp#include iostream #include func.h using namespace std; int main() { cout Add(1, 2) endl; // ① Addint → func.cpp 已实例化 → ✅ cout Add(1.5, 2.5) endl; // ② Adddouble → func.cpp 没实例化 → ❌ return 0; }解释①在模版函数定义的文件中进行显式实例化比如让其只能实例化为int其实就是将这个模版函数伪造成了一个类型为int的非模板函数②缺点就在于下次你想传递模版参数为double调用该模板函数就不适用了需要手动将显式实例化的代码改成double这就是显式实例化的硬伤。所以模版函数的声明定义放在一起才是最正确最简单最严谨的做法 [ 作者 ] shylyly [ 首次发布 ] 2024.10.8❌ [ 最新修改 ] 2026.9.28 [ 声明 ] 由于笔者水平有限文中难免有疏漏或不妥之处还望读者不吝赐教
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

低空经济无人机AI巡检系统:从设计方案到闭环落地 2026/9/29 13:14:39

低空经济无人机AI巡检系统:从设计方案到闭环落地

简介:这份《低空经济无人机AI巡检系统设计方案》面向无人机应用开发者、AI视觉工程师及工业巡检项目规划人员,系统讲解如何构建一套覆盖电力线巡检、管道监测、农田病虫害识别与城市基础设施检查的智能巡检方案。文档围绕飞行平台选型、飞控与多模式航线…

阅读更多 →
基于前向神经网络的音乐情感识别分类算法实战指南 2026/9/29 13:14:13

基于前向神经网络的音乐情感识别分类算法实战指南

简介:这是一篇面向音乐信息检索、推荐系统与情感计算方向研究者的科研论文PDF,聚焦单模态数据在音乐情感分类上的局限,提出基于前向神经网络的多特征融合分类算法。作者在传统前向神经网络隐藏层中引入切比雪夫正交多项式簇作为各神经元激励函…

阅读更多 →
改进YOLOv5船舶目标检测:小目标召回提升与锚框重聚类实战 2026/9/29 13:14:13

改进YOLOv5船舶目标检测:小目标召回提升与锚框重聚类实战

简介:这份文档面向计算机视觉方向的研究生、算法工程师及船舶检测领域从业者,系统探讨基于改进Yolov5算法的船舶目标检测方法,帮助读者理解复杂海况与多目标场景下提升检测精度与鲁棒性的完整思路。内容从研究背景与国内外现状切入&#xff0…

阅读更多 →
STM32 GPIO输入深度解析:从按键抖动到低功耗的工程实践 2026/9/29 13:14:13

STM32 GPIO输入深度解析:从按键抖动到低功耗的工程实践

1. 按键按下那一刻,GPIO 寄存器里到底发生了什么很多人第一次把按键接到 STM32 上,代码写得飞快:开时钟、配 GPIO_Mode_IPU、读 IDR、判断电平。跑起来灯也亮了,串口也打印了,于是觉得“GPIO 输入不过如此”。但真到项…

阅读更多 →
DeepSeek职场应用实战:任务分类、提示词与参数调优指南 2026/9/29 13:14:06

DeepSeek职场应用实战:任务分类、提示词与参数调优指南

简介:来自清华大学人机协同团队的《DeepSeek如何赋能职场应用?》第二讲课件,面向职场人士、管理者和人工智能应用开发者,系统梳理DeepSeek从提示语技巧到多场景应用的完整路径。资源共1个PDF文件,压缩包约9.57MB&#…

阅读更多 →
IOTE展会观察:无源物联网与边缘计算引领物联网落地新风向 2026/9/29 13:13:45

IOTE展会观察:无源物联网与边缘计算引领物联网落地新风向

1. 展会整体观察:为什么IOTE值得每年都看IOTE 2021国际物联网展会,说实话,在国内物联网圈子里属于“每年都要去一趟”的那种场合。做硬件、做平台、做方案集成的人,不管平时在群里吵得多凶,到了展会这几天,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉