【跟着操作就行了】手把手教你 编译+链接 程序环境教程

简介: 【跟着操作就行了】手把手教你 编译+链接 程序环境教程

程序的翻译环境和执行环境


在ANSI C的任何一种实现中,存在着两种不同的环境。


也就是说,我们写出C语言的代码想编译成可以运行的程序,就必须要经过这两种环境。


第一种是翻译环境,在这一环境中源代码被翻译成可执行的机器指令。


另一种是编译环境,用来实际执行代码。


翻译成图大概就是这个样子。


e1ca73d076d1c8d95db1c9d1c153c9f0_05e0bbe1a8184aaba6305963ab719f4f.png


其中,test.exe当然就是可执行的二进制指令,也被称为上面所说的机器指令。


详解编译+链接


翻译环境


我们知道,一个程序可以有许多源文件。编译器可以将这众多的源文件转换为目标代码。


而要把这众多的目标文件链接到一起,就需要链接器(linker)来大展身手了!


链接器可以将众多的目标文件捆绑到一起,形成一个单一而完整的可执行程序。


我们可以搜索到编译器和链接器。


63cc7eeb719fdb649d1627848b78db60_a39b1c9e470b4caea61d1244da2ae4c3.png

07bf3b86dbcd5a918b1ca4e927e43879_20ee744b3f994130b6dc98db7a7ff18b.png



图一是编译器,图二是链接器。


链接器同时也会引入标准C函数库中被该程序所用到的函数,它还可以搜索程序员个人 的程序库,并且将其需要的函数也链接到程序中。


我们想观察到编译和链接的过程,就需要用到gcc编译器,为什么不能用VS呢?


因为VS是一个集成开发环境,集成很多功能,一个ctrl+f5直接出结果了,不方便观察编译和链接细节。


编译本身也分为几个阶段


预编译(预处理)


翻译环境要经过编译和链接两个阶段,编译的过程又分为预编译,编译和汇编。


画成图的话就是下面这样(我 的 肝 好 累)(;´༎ຶД༎ຶ`)


390fe689f72b21066e6d5cf0c4a8f56c_7d4b437427174923927584ebbd1c7576.png


我们先在gcc编译器搭建好环境。


266b47116bd098789d300b7eccfd2f79_1f0be656b8fd43538c10590352216b29.png


我们想让test.c文件只是预编译,而不执行后面的操作,那就要这么写命令行。


gcc  test.c -E -o test.i


9a80b19a298f0559ea2ee5c17d3e02d4_e4a179a86d064a178abc50debc187ea0.png

9d4003ea31a6e5d0c8085ce757573eac_bcfe667407c748ac9d73b1763e900516.png


这样我们就将预编译的内容写到了一个叫test.i的文件里面。


只写gcc test.c -E -o的话,上面那一长串代码就会打印到终端上面。


f3274e64e682e0b3513f2c5c054e250d_c04369c514ed4632aa200421ccb82740.png


我们在这一串代码中发现了老朋友头文件stdio.h的身影。


那就引出了预编译的第一个功能——头文件的包含。


01a20a4469a34e6d94260ec00d938dbd_ad383f309805453e8615eeefc9de631c.png


上面是test.i文件里存放的预处理之后的代码。我们发现,SZ被替换成了10,也是它被定义的值。


我们就发现了预编译的第二个功能——#define所定义的符号的替换。


预编译的第三个功能就直接写出了——注释的删除


上面的三个文本操作确实会让代码的行数减少很多。


编译


要观察编译的过程,我们写出这样的命令行。


a86f5bd2a13f152ee20dae950b2efa51_d34b2e5271ae4a5aa2b5eaf4b34d4ffe.png


运行命令,发现跳出一个叫test.s的文件。


这个文件里的内容和汇编指令十分相似。


1346f3ee2db2a532637815936ef127ec_3b945067452f4dc9a25d01dc5253d8c4.png


所以编译阶段所做的事情就是把C语言代码翻译成汇编代码。


当然这之间的事情十分的复杂,要经过语法分析,词法分析,语义分析,符号汇总的操作。


大家可以去购买相关的书籍查阅,像《程序员的自我修养》里面就有相关的解析。


其中的符号汇总我下面会讲。


汇编


我们接着写出这样的命令行  gcc test.s -c。


08b847152ac7402ebda4023f32a4b321_9d576b9305fa4b1ab3d6487fbecabc46.png


弹出一个 test.o 文件。


在VS编译器上面,目标文件的后缀是.obj,而在gcc编译器,目标文件的后缀是.o。


所以test.o这个文件就是目标文件了。


目标文件是什么格式呢。


e28d4929f3db8dea6471410cd6e27dad_90e4a4799fd64ddd8f80cf3ae7c311ae.png


我们点开这个文件,发现它是二进制文件。


我们就明白了,汇编所做的事情就是将编译出来的汇编代码转换成二进制指令。


链接


链接过程就是将众多上面的test.o文件编译成test.exe,也就是可执行程序。


输入gcc test.o来直接编译test.o文件。


61de117fb51fb18d8569b8b48bf86868_bfa2589bcf404156b72311dcea84e0fa.png


生成一个a.exe文件。


直接运行a.exe文件。


87b8e2a3d86c50ac2c1896cf463f46f3_46a02b9aaf704a0a8e7d27b02743ec16.png


同样可以达到test.exe的效果。


上面说了, 链接过程就是将众多上面的test.o文件编译成test.exe,也就是可执行程序。


那如何编译呢?要经过两个过程:


1.合并段表


2.符号表的合并和重定位。


我就以下面的代码为例来讲解。


01d563bbbcdeba76263602c22e6c7b34_1234d09da5754d948816f5e36634a7ef.png


我们已经知道,上面的代码经过汇编会产生两个 .o 文件,我们就假设一个为Sub.o,另一个为test.o。


什么是符号汇总?


在编译阶段,程序会进行符号汇总的操作。


将函数声明和全局变量等进行汇总,其实就是收集起来。如图所示。


cdac46b0cecb696841ae6a3e9556d12c_2edd346df82f45c59c62058d4319df07.png


当完成符号汇总操作之后,程序就该进入汇编阶段了。


汇编阶段就是将编译出来的汇编代码转换成二进制指令。


这里面就包含了形成符号表的操作。


805d1d70922c17485d57ad325f03d05e_0d87b0fd3af64043a85bc41ab198b011.png


我们给收集来的符号假定几个地址,其中test.o里面的Sub给上空指针。


接下来就该合并段表和符号表的合并和重定位了。


合并段表



6576acbc1d2f2f00a39f99208c0b9a03_9f471ea725d042b1a442d213cc87550d.png


各目标文件需要链接库来形成可执行程序。


目标文件,已经是二进制的文件。它们是有格式的。


就以gcc编译产生的目标文件为例,目标文件的格式是elf的文件格式。


可支持程序文件的格式也是elf的。


这种文件格式会把文件分成段。


编译器就把这对应段的文件合并在一起,形成可执行程序。


479083458eee907126d677d909b92184_704f7496a0764d44ad666bdd522f8b7b.png


当然,这一块的操作是非常复杂的。


符号表的合并和重定位


Sub.o文件和test.o文件各有各的符号表,难道生成的文件里也是各有各的文件表吗?


当然不是,它们要进行符号表的合并和重定位操作。


b0392859d3593138e4e7e9dbea827636_69427d9a5cd8473cb3db1d49848bc5bc.png


无效的地址会被清除,从而形成一个新的符号表。


它有什么用呢?


当我们删除声明的Sub函数,读取的符号表会变成main函数和空指针的Sub函数声明


我们通过这个空指针函数声明去找未定义的Sub函数时,就会报出链接时错误——无法解析的外部符号。

0cf03a9cbbd68535ebf55d9e883a4147_c927538ed0674b0884f9e9174b00a980.png

a1d3e9b8e8b36193de2109fca1d0d656_67b36a7d62c24afab00f9c8211eda3db.png


如果函数的名字写错也是同理。


总结

 感谢观看,本文到这里就结束了,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹


cad3f4971ac28288f5f73c67c1f7b77b_7673ea0a00c3431893891e0c2913a10e.jpeg


 也欢迎你,关注我。👍 👍 👍


 原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!!💕 💕 💕 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。🎉


相关文章
|
设计模式 C++
设计模式之抽象工厂模式(C++)
设计模式之抽象工厂模式(C++)
243 0
|
5月前
|
数据采集 存储 运维
什么是数据中台?看这篇就够
在数字化时代,企业数据激增却难见效?根源在于缺乏数据中台。它不仅是技术平台,更是融合数据采集、治理、服务与运营的体系,打破孤岛,提升效率,驱动业务创新。本文带你全面了解其定义、搭建步骤与核心价值,助力企业真正实现数据赋能。
什么是数据中台?看这篇就够
中断向量表的作用是什么?
【10月更文挑战第28天】中断向量表在计算机系统中扮演着至关重要的角色,它是实现中断处理、优先级管理、系统初始化以及硬件与软件交互的核心机制。通过中断向量表,计算机系统能够高效地响应各种中断事件,保证系统的稳定性、可靠性和实时性,为计算机的正常运行和各种应用程序的执行提供了有力支持。
1276 60
|
消息中间件 Java Spring
SpringBoot实现RabbitMQ的广播交换机(SpringAMQP 实现Fanout广播交换机)
SpringBoot实现RabbitMQ的广播交换机(SpringAMQP 实现Fanout广播交换机)
350 2
|
8月前
|
人工智能 安全 编译器
华为自研仓颉编程语言将开源,未来与ArkTS同等地位
在2025年华为开发者大会上,华为正式推出HarmonyOS 6 Developer Beta,并宣布仓颉编程语言将于7月30日开源。作为定位下一代的编程语言,仓颉具备高效开发、安全可靠、轻松并发与卓越性能等特性,支持多范式编程与全面工具链,助力鸿蒙生态构建。
613 1
华为自研仓颉编程语言将开源,未来与ArkTS同等地位
|
存储 安全 BI
PeopleSoft中的文件上传与下载:实现与优化
PeopleSoft中的文件上传与下载:实现与优化
425 7
|
机器学习/深度学习 弹性计算 人工智能
阿里云服务器ECS架构区别及选择参考:X86计算、ARM计算等架构介绍
在我们选购阿里云服务器的时候,云服务器架构有X86计算、ARM计算、GPU/FPGA/ASIC、弹性裸金属服务器、高性能计算可选,有的用户并不清楚他们之间有何区别,本文主要简单介绍下这些架构各自的主要性能及适用场景,以便大家了解不同类型的架构有何不同,主要特点及适用场景有哪些。
1831 10
|
安全 JavaScript
如何在`package.json`中正确设置依赖版本范围?
正确设置 `package.json` 中的依赖版本范围需要综合考虑项目的需求、依赖库的稳定性和兼容性,以及开发和维护的便利性等因素。通过合理选择版本范围符号,并结合定期的审查和测试,可以有效地管理项目依赖,确保项目的稳定运行。
554 1
|
前端开发
app下载介绍页HTML源码
APP下载页前端自适应HTML源码,可以作为自己的软件介绍页或者app下载页,喜欢的朋友可以拿去研究
549 2
app下载介绍页HTML源码