【C语言进阶】编译链接

news2024/11/14 21:24:45

在这里插入图片描述

文章目录

  • 📖程序的两种环境
    • 🔖翻译环境
    • 🔖执行环境
  • 📖详解翻译环境
    • 🔖从人的角度去看编译链接
    • 🔖预编译
    • 🔖编译
    • 🔖汇编
    • 🔖链接
    • 🔖符号表的作用
  • 📖执行环境

📖程序的两种环境

 在ANSI C的任何一种实现中,都存在两种环境

  • 翻译环境
  • 执行环境

 ANSI就是美国国家标准协会的简称,而ANSI C就是美国国家标准协会创立的一套C标准,该标准于1989年完成,这个版本的语言经常被叫做ANSI C有时也称为C89

🔖翻译环境

 在这个环境下源代码被转换成可执行的机器指令。我们平时用VS等工具写出来的源代码都是由字符组成的,只有我们人才能读懂其中的意思,机器是不能直接读懂的,机器只能执行二进制指令,因此就需要把我们写的源文件变成机器指令。而我们写的以 .c 结尾的源文件就是经过翻译环境才得以变成以 .exe 结尾的可执行程序(里面包含的就是可执行指令)。
在这里插入图片描述

 在VS中由源代码生成一个可执行程序的过程如下图所示:

在这里插入图片描述
在这里插入图片描述
 如上图所演示的,VS2019其实就充当了翻译环境,VS2019是一种集成开发环境。

🔖执行环境

 执行环境是用于实际执行代码的。

📖详解翻译环境

 编译和链接是翻译环境的两大组成部分,编译环境又细分了预编译、编译、汇编三个小部分,下面我用一张图来展示它们之间的关系。
在这里插入图片描述
注意: 上图仅仅展现了它们之间的包含关系,并没有反映出它们的执行顺序,正确的执行顺序是:预编译、编译、汇编、链接。

🔖从人的角度去看编译链接

 作为程序员我们只需要将写好源代码交给编译器最终就会得到我们想要的结果,我们无需关心编译器到底进行了哪些操作。具体的过程是:编译器会对我们写的每一个源文件进行单独处理得到一个目标文件,再经链接器把这些所有的目标文件链接到一起,最终得到一个可执行程序
在这里插入图片描述
 其中链接库就是指将库文件编译后打包为一个二进制文件,这些二进制文件会在程序调用的时候加载到内存。

在这里插入图片描述
在这里插入图片描述
 如上图,此时我们的工程下由两个源文件,分别是text.cadd.c,在生成解决方案后,我们可以在相应的路径下看大它们分别对应的.obj文件。
 了解了从人的角度去看编译链接接下来就让我们深入计算机的底层去看看编译链接的“庐山真面目”。

🔖预编译

 为了更加直观的展示编译过程的细节,后面我将利用gcc这款编译器进行演示
 首先创建一个text.h文件和一个text.c文件,如下图所示:

在这里插入图片描述

 要得到预处理后的文件需要用下面这条命令:gcc -E text.c -o text.i

  • gcc表示用gcc这个编译器
  • -E表示执行完预处理就停止下来
  • -o text.i表示生成一个text.i文件来存放预处理后得到的结果

在这里插入图片描述

 通过对比,我们可以发现text.i文件相对于text.c文件主要发生了以下几个方面的改变

  • text.i文件中把text.h文件中的内容拷贝了过来。
  • 其次在text.i文件中对#define定义的标识符常量进行了替换和删除
  • 注释的删除。

 为什么会出现这种结果?因为#include#define都叫做预处理指令,跟预处理指令相关的操作都会在预编译阶段做处理,并且这些处理都是一些文本操作(内容的拷贝、宏的替换和删除、注释的删除)

🔖编译

 预编译结束接下来就该进行编译了,要得到编译后的文件需要用到下面这条命令:gcc -S text.i

  • 其中-S表示编译结束后就停下来。
  • text.i也可以换成text.c
  • 这里不需要-o选项,因为编译器默认会生成text.s文件,当然这里我们也可以用-o选项指定生成的文件名,这里大家可以自行尝试。

在这里插入图片描述
 看不懂,根本看不懂,这text.s文件中放的到底是啥呀?😭,其实这些都是汇编指令。总结:编译其实就是把C语言代码翻译成了汇编代码,但这仅是我们通过观察现象得到的结论,编译过程究竟干了什么呢?总结一下,其实编译过程干了下面几件事:

  • 语法分析(检查是否有语法错误)
  • 词法分析(会把代码肢解开形成一颗语法树)
  • 语义分析(分析每段代码是干嘛的)
  • 符号汇总(会把代码中涉及到的一些符号,例如:函数名全局等的符号汇总下来,在下一步汇编中使用,不关心局部变量,因为局部变量只能在局部范围使用)
  • 做完上面四件事产生的结果就是得到汇编代码

🔖汇编

 编译结束接下来就到汇编了,汇编需要用到下面这条指令:gcc -c text.s

  • 其中-c表示编译结束就停下来。
  • 这里不需要-o选项,因为编译器默认会生成text.o文件,当然这里我们也可以用-o选项指定生成的文件名,这里大家可以自行尝试。
  • 这里.o结尾的其实就是在gcc环境下生成的一个目标文件,在VS中生成的目标文件后缀是.obj

在这里插入图片描述
 通过上图可以看出目标文件是一个二进制文件,这意味着:汇编过程把汇编代码翻译成了二进制指令。同样这是我们通过观察得出来的结论,汇编过程最重要的是形成符号表,这和编译阶段执行的符号汇总是相关联的,符号表把编译阶段汇总的符号与其地址对应起来形成了一张表,这张表就被叫做符号表。符号表在链接这个阶段还要被使用。

🔖链接

 链接阶段主要干了下面两件事:

  • 合并段表(编译得到的目标文件都是一个独立的ELF文件)
  • 符号表的合并和符号表的重定位

 上面提到过,编译器对每个源文件是进行单独处理的,最终每个源文件都会得到一个目标文件,因此每个目标文件都会有一个符号表,以下面的程序为例:

在这里插入图片描述
text.obj中的符号表记录了Addmainadd.obj中记录了Add,但实际上在text.c中没有Add函数的定义,所以text.obj中的符号表并不知道Add函数的真实地址,而add.obj中的符号表,则记录了Add函数的正确地址。在进行符号表合并的时候,两个表中都有Add和地址的对应关系,但text.obj中的对应关系一定是错误的,所以就会舍弃它,保留add.obj中正确的Add和地址的对应关系,同时mani和地址的对应关系也会被保留,最终的可执行程序中的符号表就是合并后的符号表。
在这里插入图片描述

🔖符号表的作用

 符号表记录了一些函数名、全局的符号和地址的对应关系,最终汇总符号表可以帮助我们实现跨文件函数调用,就像上面的程序,我们在text.c中通过extern关键字声明了外部符号Add,然后就可以在当前的源文件里去调用add.c中的Add函数,其实没有这条声明语句程序也能正常运行,因为就算声明了,text.obj中记录的也是一条无意义的地址对应关系,最终在汇总符号表的时候还是会被删除。但是注意:如果要使用另一个源文件中的全局变量,是一定要声明的,否则会报错。如果没有用extern去声明,在编译阶段进行语法分析的时候就会报错。这里函数和全局变量的差异仅仅是编译器自己对这两种情况的处理方式有所不同,正确的做法是只要使用了另一个源文件中的函数、全局变量等都要通过extern进行声明。
 再通过下面的程序看看符号表的作用

在这里插入图片描述

在这里插入图片描述
text.obj中的符号表记录了一条add和地址的对应关系,最终会汇总到总的符号表当中,当然这个对应关系是不存在的,因为我们压根就没对add进行任何定义,最终到这个“虚假”的地址里面当然就什么都找不到,自然就报了“链接错误

📖执行环境

 程序的执行主要有以下几个过程:

  • 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统来完成。在独立的环境中,程序的载入必须由手工安排,也可以是通过可执行代码置入只读内存来完成(其实就是平时我们把代码下载到单片机板子上的这个过程)。
  • 程序的执行便开始,接着便调用main函数。
  • 开始执行程序代码。这个时候程序将使用一个运行时堆栈(Stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储在静态内存中的变量在整个程序执行的过程中都会保留他们的值
  • 终止程序。正常终止main函数,也有可能是意外终止。

在这里插入图片描述
 如上图,我们双击以.exe结尾的可执行文件就会进入到执行环境,此时程序已经被加载到内存中。


 今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是春人前进的动力!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/672577.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

外贸订单管理平台有哪些?

外贸订单的管理是外贸出口业务中非常重要的一项管理工作,订单能否实现准时交付则需要涉及到各种流程顺畅的支持。那么外贸订单管理平台有哪些?有孚盟软件。 首先,外贸订单管理平台主要是解决外贸公司的订单查询与管理,面对大量不…

2.4G无线收发芯片 XL2400,SOP8封装,外挂MCU使用

XL2400 芯片是工作在 2.400~2.483GHz 世界通用 ISM 频段的单片无线收发芯片。该芯片集成射频收发机、频率收生器、晶体振荡器、调制解调器等功能模块,并且支持一对多组网和带 ACK 的通信模式。发射输出率、工作频道以及通信数据率均可配置。芯片已将多颗外围贴片阻容…

mysql错误-1055 - Expression #1 of SELECT list is not in GROUP BY clause 解决方案

目录 业务场景发现问题表结构表数据sql查询 分析问题验证 解决问题方案一方案二方案三 注意事项 业务场景 当遇到数据库重复数据,就要将数据进行分组,取其中一条来展示,此时就要用到group by语句。 但当mysql的版本高于5.7时,在执…

ESP32设备驱动-TCA9548A-I2C多路复用器驱动

TCA9548A-I2C 多路复用器驱动 文章目录 TCA9548A-I2C 多路复用器驱动1、TCA9548A介绍2、硬件准备3、软件准备4、驱动实现4.1 TCA9548A总线复用实现4.2 驱动多个OLED显示屏4.3 驱动多个BME280传感器在本文中,将介绍如何使用 TCA9458A I2C 多路复用器扩展 I2C 总线端口。 如果你…

【Python 基础篇】Python 列表及列表常用函数

文章目录 一. 什么是列表二. 列表的创建和访问2.1 创建列表2.2 访问列表元素 三. 列表的操作3.1 修改列表元素3.2 列表的添加和删除元素3.2.1 添加元素3.2.2 删除元素 3.3 列表的排序3.4 判断元素是否在列表中 四. 列表的常用函数和方法五. 总结 一. 什么是列表 在Python中&am…

CLO-GitHub网站注册登录

一、GitHub GitHub是一个面向开源及私有软件项目的托管平台,仅支持Git作为唯一的版本库格式进行托管。 二、登录官网 网站:https://github.com/ 2.1 注册Sign up Enter your Email(输入邮箱地址)->Continue-> ->Create a…

HCIP网络笔记分享——VLAN及MPLS多标签协议交换

第三部分 HCIA回顾一、MPLS多协议标签交换1、标签交换2、静态LSP搭建3、LDP协议4、本地LDP会话建立的过程 二、MPLS VPN的配置使用MPLS VPN 三、实际情况配置1、VLAN第一步:创建VLAN第二步:将接口划入VLAN第三步:配置trunk干道 2、U/T标记3、…

AI绘画基于 Kaggle 10 分钟搭建 Stable Diffusion(保姆级教程)

AI绘画基于 Kaggle 10 分钟搭建 Stable Diffusion(保姆级教程) 一、引言二、安装教程1. 注册 Kaggle2. Edit My Copy3. 进行手机号的验证4. 打开 “internet off” 开关,并选择显卡5. 开启 session,运行脚本 三、主界面介绍四、注…

车辆派遣管理系统——需求

文章目录 一、背景二、用户的特点三、系统范围四、系统体系结构系统总体架构描述: 五、数据库设计E-R图数据字典 六、工作量估算七、项目燃尽图 一、背景 车辆派遣管理系统是提供对车辆派遣高效管理的系统,自动生成结算结果;可以有效节约车辆…

侯捷——1.C++面向对象高级开发 总结

侯捷——1.C面向对象高级开发 总结 前面的几个视频没有总结,等以后有空再补 7. Class with pointer member(s) —— string 类 该string类,内含指针,所以要自己写构造函数和析构函数,不能使用默认的构造函数和析构函数。 包含…

MHA部署

目录 高可用集群MHA 环境准备 设置服务器免密通联 修改主机名和hosts文件(四台) 四台服务器相同操作 四台服务器相同操作-关闭防火墙 四台服务器-免密配置-生成密钥 四台服务器-免密配置-复制密钥 将authorized_keysau文件发送给node2/3/4 测试是否免密登陆其他主机 …

0018-TIPS-pawnyable : NULL-Pointer-Dereference

原文 NULL Pointer Dereference 题目下载 环境宽松限制 启动选项中可以看到,SMAP在被攻击的机器上被禁用。除非禁用 SMAP,否则无法利用本章中讨论的 NULL 指针取消引用。 还可以尝试启动该内核并输入以下命令: $ cat /proc/sys/vm/mmap_mi…

chatgpt赋能python:Python文件打包:了解基本概念和步骤

Python文件打包: 了解基本概念和步骤 Python开发现在已经越来越成为了一种主流开发方式,然而,当我们的代码在多个环境中运行时,我们可能会遇到各种问题。Python文件打包就起到了非常重要的作用,它可以把我们的代码和环…

java ArrayList()常用详解

文章目录 java ArrayList()常用详解构造方法构造一个具有指定初始容量的空列表构造具有指定初始容量的空列表 常用方法指定的元素追加到此列表的末尾泛型 用来约束集合中存储数据的数据类型在次列表中的指定位置插入指定的元素返回此列表中指定位置的元素删除该列表中指定位置的…

spring 解决循环依赖

spring 解决循环依赖 1、什么是循环依赖? 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图: spring的单例对象的初始化主要…

DAC0832

芯片简介: DAC0832是采样频率为八位的D/A转换芯片,集成电路内有两级输入寄存器,使DAC0832芯片具备双缓冲、单缓冲和直通三种输入方式。D/A转换结果采用电流形式输出。若需要相应的模拟电压信号,可通过一个高输入阻抗的线性运算放…

Redmi Book Pro 15 2022 笔记本电脑更换硬盘总结

我的电脑型号是Redmi Book Pro 15 2022 锐龙版,早在6月15日磁盘空间就告急了,正好遇上618就打算更换硬盘,直到今天更换成功,特此来记录一下 目录 一、更换硬盘前准备工作二、更换硬盘三、硬盘迁移以及分区四、总结 本文的更换过程…

实验记录之——mac用visual studio code配置opencv

安装opencv 用Homebrew安装opencv brew install wget brew install cmake brew install opencv看来都安装过了hhh 通过下面命令再次检测安装的opencv版本 brew info opencv全部都打绿色的勾,应该就是都安装完了。 Vscode配置c/c环境 配置cpp项目可以看下面链接…

第一天,PyTorch张量的运算

文章目录 一、说明二、张量的常用运算1. 索引和切片(与numpy类似)2. 通过torch.cat来进行连接张量3. 矩阵运算和算数运算1. 在PyTorch中mul与matmul和的区别2. 矩阵运算和算数运算的示例 4. 聚合张量中的所有值5. 给所有元组增加1,使用add_&a…

Chrome 开发者调试常用工具

Chrome调试工具介绍 ①②③④⑤⑥⑦⑧⑨⑩ 一、Elements-元素 选项说明 styles filter搜索框:查找过滤样式, 包括class名当前选中的元素 查看其伪类效果已经样式给当前选中元素添加class名可以把当前元素class名或者id, 按照css层级添加一…