【C语言】 —— 编译和链接

news2024/11/13 11:32:38

【C语言】 —— 编译和链接

  • 一、编译环境和运行环境
  • 二、翻译环境
    • 2.1、 预处理
    • 2.2、 编译
      • (1)词法分析
      • (2)语法分析
      • (3)语义分析
    • 2.3、 汇编
    • 2.4、链接
  • 三、运行环境

一、编译环境和运行环境

  平时我们说写 C语言 代码,写程序,不难发现:其实写出来的都是 t e s t test test. c c c t e s t test test. h h h 等源文件和头文件。我们直接打开他们,是可以直接看懂的,这也就说明了他们其实是文本文件。但是计算机是看不懂他们的,计算机只能识别二进制指令,无法对文件中的代码直接执行。这时就需要将 C语言 代码进行处理变成二进制的指令。
  
  而将代码处理成二进制指令正是编译器需要做的事情。
  
ANCI C 的任何一种实现中,存在两个不同的环境:

  1. 翻译环境:在这个环境中源代码被转换成可执行的机器指令(二进制指令)
  2. 执行环境:它用于实际执行代码

  

二、翻译环境

  那翻译环境是怎么将源代码转换为可执行的机器指令的呢?这里我们就得展开讲解一下翻译环境所做的事情
  
  其实翻译环境是由编译链接两大过程组成,而编译又可以分解成:预处理(有些书也叫预编译)、编译汇编三个过程。

在这里插入图片描述

  
一个C语言的项目中可能由多个 . c c c 文件一起构建,那多个 . c c c 文件如何生成可执行程序呢?

  • 多个 . c c c 文件单独经过编译器,编译处理生成对应的目标文件
  • W i n d o w s Windows Windows 环境下的目标文件的后缀是 .obj,在 L i n u s Linus Linus环境下目标文件的后缀是 .o
  • 多个目标文件链接库一起经过连接器处理生成最终的可执行程序
  • 链接库是指运行时库(它是支持程序运行的基本函数集合)、C语言库函数或者第三方库

  这里需提一下,我们所用的 V S 2022 VS2022 VS2022 是一种集成开发环境,包括:编辑器编译器链接器调试器。而 c l cl cl. e x e exe exe 是其编译器, l i n k link link. e x e exe exe 是其链接器
  

在这里插入图片描述

  我们可以在 L i n u s Linus Linus 服务器、 g c c gcc gcc 的环境下对链接和编译各个阶段进行观察
  
  

2.1、 预处理

  在预处理阶段,源文件和头文件会被处理成 .i 为后缀的文件。

  在 g c c gcc gcc 中,将 .c 文件处理成 .h 文件,命令如下:

gcc -E test.c -o test.i

  
  预处理阶段主要处理那些源文件中 # 开始的编译指令。比如:# i n c l u d e include include,# d e f i n e define define,处理的规则如下:

  • 将所有的 # d e f i n e define define 删除展开所有的宏定义
  • 处理所有的条件编译指令,如:#if#ifdef#elif#else#endif
  • 处理 # i n c l u d e include include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
  • 删除所有注释
  • 添加行号文件名标识,方便后续编译器生成调试信息等
  • 保留所有的 # p r a g m a pragma pragma 的编译器指令,编译器后续会使用

  经过预处理后的 . i .i .i 文件不再包含宏定义,因为宏已经被展开。并且包含的头文件被插入.i 文件中。所以我们无法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的 . i .i .i 文件来确认。
  
t e s t test test. c c c 文件

在这里插入图片描述

  
t e s t test test. i i i 文件

在这里插入图片描述

   . i .i .i 文件只有输入指令生成后我们才能看到,正常情况下生成中间文件编译器用完就删掉了。
  
  

2.2、 编译

  编译过程就是将预处理后的文件进行一系列的:词法分析语法分析语义分析及优化,生成相应的汇编代码文件

编译过程的命令如下:

gcc -S test.i -o test.s

  编译过程最终会生成 .s 的文件,它里面放的是汇编代码。其实编译阶段整体上就是将 C 代码转换成汇编代码
  
t e s t . s test.s test.s 文件

在这里插入图片描述

  那编译过程具体是做了什么工作呢?
  他们分别是:词法分析语法分析语义分析及优化
  下面让我们一起简单了解
  
  假设有下面的代码,在进行编译时会时编译器怎么做呢

array[index] = (index + 4) * (2 + 6);

  

(1)词法分析

  将源代码程序输入扫描器,扫描器的任务就是简单的进行词法分析把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。
  

上述代码进行词法分析后得到了 16 个记号:

记号类型记号类型
array标识符4数字
[左方括号)右圆括号
index标识符*乘号
]右方括号(左圆括号
=赋值2数字
(左圆括号+加号
index标识符6数字
+加号)右圆括号

  

(2)语法分析

  接下来则是语法分析语法分析器会对扫描产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点的树

在这里插入图片描述

  

(3)语义分析

  由语义分析器来完成语义分析,即对表达式的语法层面分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配。这个阶段会报告错误的语法信息

在这里插入图片描述

  

2.3、 汇编

  汇编是指通过汇编器将汇编代码转变成机器可执行的指令,每一个汇编语句几乎都对应一条机器指令。就是按照汇编指令和机器指令的对照表一一的进行翻译,也不做指令优化

  汇编的命令如下:

gcc -c test.s -o test.o

  经过汇编处理后文件即为目标文件( . o b j .obj .obj / . o .o .o)目标文件为二进制文件,无法通过文本编辑器打开

  

2.4、链接

  链接是一个复杂的过程,链接的时候需要把一堆文件链接在一起才生成可执行程序。
  链接的过程主要包括:地址和空间分配符号决议重定位等这些步骤。
  
  链接主要解决的是一个项目中多个文件、多模块之间互相调用的问题
  比如:现在有两个文件( t e s t . c test.c test.c a d d . c add.c add.c
  
t e s t . c test.c test.c 文件

#incldue<stdio.h>

//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(10, 20);
	printf("%d\n", c);
	return 0;
}

  
A d d . c Add.c Add.c 文件

int g_val = 2024;

int Add(int x, int y)
{
	return x + y;
}

  
  为什么在 Add.c 中定义的文件在 test.c 文件中声明一下就可以使用呢?
  这里进行简单的了解
  
  经过前面的学习,我们知道每一个源文件( . c .c .c)经过编译过程后都会生成自己的目标文件 . o .o .o / . o b j .obj .obj
  
  在编译的过程中,会对代码中的符号进行符号的汇总,并形成相应的符号表,符号表中会存储符号相对应的地址。在产生 t e s t . c test.c test.c 文件的符号表时,遇到只有声明而未定义的符号 Addg_val 时,会暂时将其地址搁置

在这里插入图片描述

  

  到了编译过程,编译器会将多个目标文件链接在一起(目标文件的格式是一样的,并且是分段的形式),从而生成可执行程序(可执行策划给你续最终也是如目标文件一样的分段形式)
  
  在合并过程中,符号表也需要合并成一份。合并后的符号表每个符号只能有一份,那 Addg_val 符号自然用其有效地址的那一份,这样符号表就完成了合并。通过 A d d Add Add 的地址,就自然而然能找到 A d d Add Add 函数了。
  

在这里插入图片描述

  而上述对地址的修正过程被叫做:重定位
  
  前面我们非常简洁的讲解了一个 C 的程序是如何编译和链接,到最终生成可执行程序的过程,其实很多内部的细节无法展开讲解。
  比如:目标文件的格式 e l f elf elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看 《程序员的自我修养》 一书来详细了解。
  
  

三、运行环境

  运行环境实际上是非常复杂的,我们这里简单了解了解

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手动安排(单片机烧板子),也可以通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用 m a i n main main 函数
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(函数栈帧),存储函数的局部变量返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
  4. 终止程序。正常终止 m a i n main main 函数;也有可能是意外终止

  
  
  
  
  


  好啦,本期关于编译和链接的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!

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

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

相关文章

【电机控制】EG2134无刷电机驱动、控制一体板——开环、无感SMO验证

【电机控制】EG2134无刷电机驱动、控制一体板——开环、无感SMO验证 文章目录 前言一、硬件二、软件三、开环SVPWM四、SMO无感观测器闭环控制五、参考文献总结 前言 【电机控制】直流有刷电机、无刷电机汇总——持续更新 【电机控制】EG2134无感FOC驱控一体板-滑模观测器 使用…

详解Linux的shell脚本基础指令

一、shell简介 是Linux系统的用户界面&#xff0c;它提供用户与内核的一种交互方式。它接收用户输入的命令&#xff0c;并把它送入内核去执行&#xff0c;是一个命令解释器。 脚本&#xff1a;本质是一个文件&#xff0c;文件里面存放的是 特定格式的指令&#xff0c;系统可以…

数学系C++(六七)

目录 * &指针与地址 void指针 指针可以等于&#xff1a; const 指向常量的指针 const int *px 常指针 int * const px 指向常量的常指针const 类型标识符 * const 指针名 指针加减&#xff1a; 指针恒等式 函数指针【待续】 指针型函数&#xff1a; 指向函数的…

【mindspore进阶】02-ResNet50迁移学习

Mindspore 应用&#xff08;2&#xff09;ResNet50迁移学习 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少有人会从头开始训练整个网络。普遍的做法是&#xff0c;在一个非常大的基础数据集上训练得到一个预训练模型&#xff0c;然后使用该模型来初始化…

关于Python的电影信息爬取与数据可视化分析报告

目录 1 引言 1.1 研究背景 1.2 研究目的 1.3 研究意义 2 相关技术介绍 2.1 Python语言及其应用领域 2.2 网络爬虫技术 2.3 数据可视化技术 2.4 PyCharm 2.5 Jupyter Notebook 3 数据爬取 4 数据预处理 5 数据分析及可视化 5.1数据分析背景 5.2从电影评分角度分析…

嵌入式开发SPI基本介绍与应用

目录 #SPI通信协议 #SPI基础概念 #SPI通信模式 #SPI通信时序类型 前言&#xff1a;本篇笔记参考嘉立创的开发文档&#xff0c;连接放在最后。 #SPI通信协议 #SPI基础概念 Serial Peripheral Interface 缩写SPI 翻译&#xff1a;串行外设接口 同步串行通信协议&…

运维系列.Nginx配置中的高级指令和流程控制

运维专题 Nginx配置中的高级指令和流程控制 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/…

镭速实现大文件传输软件预览功能

在当前的数字时代&#xff0c;大文件传输软件成为了提高工作效率和文件管理便捷性的关键工具。镭速作为其中的一员&#xff0c;以其独特的功能和优势&#xff0c;为用户提供了多样化的文件预览和传输解决方案。 目前镭速支持对文档格式文件&#xff0c;一般图片格式及视频格式…

非比较排序 计数排序

1.核心思路 首先要找出max 和 min&#xff0c;最大值 - 最小值 1&#xff0c;就可以计算出数据在什么范围然后创建计数数组大小&#xff0c;a[i] - min 在数组的相对位置计数 通过自然序列排序然后把计数好的值&#xff0c;按照顺序依次放回原数组即可 动图解释&#xff0c;其…

Nettyの源码分析

本篇为Netty系列的最后一篇&#xff0c;按照惯例会简单介绍一些Netty相关核心源码。 1、Netty启动源码分析 代码就使用最初的Netty服务器案例&#xff0c;在bind这一行打上断点&#xff0c;观察启动的全过程&#xff1a; 由于某些方法的调用链过深&#xff0c;节约篇幅&#xf…

Nuxt框架中内置组件详解及使用指南(二)

title: Nuxt框架中内置组件详解及使用指南&#xff08;二&#xff09; date: 2024/7/7 updated: 2024/7/7 author: cmdragon excerpt: 摘要&#xff1a;“本文详细介绍了Nuxt 3中和组件的使用方法&#xff0c;包括组件的基本概念、属性、自定义属性、获取引用以及完整示例&a…

利用redis Zset实现 排行榜功能 配合xxl-job持久化每一个赛季的排行榜

zset 可以排序 使用xxl-job实现定时任务 对历史排行榜持久化到数据库 排行榜有当前赛季排行版和历史排行榜 当前赛季排行榜利用redis 中的SortSet 数据结构 获取 每个月的 月初 利用xxl-job的定时任务持久化化上一个月的排行榜信息 并删除redis中的数据 当排行榜数据量巨大时…

【技术追踪】GeCA:高分辨率医学图像合成的神经元胞扩散(MICCAI-2024)

扩散方法与传统方法相结合&#xff0c;挺有意思~ 本文提出一种称为生成式元胞自动机 (Generative Cellular Automata&#xff0c;GeCA) 的新模型系列&#xff0c;其灵感来自于生物体从单细胞进化而来的过程&#xff0c;显著提高了11 种不同眼科疾病分类任务的表现。 论文&#…

k8s 部署 springboot 项目内存持续增长问题分析解决

写在前面 工作中遇到&#xff0c;请教公司前辈解决&#xff0c;简单整理记忆博文内容涉及一次 GC 问题的分析以及解决理解不足小伙伴帮忙指正 &#x1f603;,生活加油 99%的焦虑都来自于虚度时间和没有好好做事&#xff0c;所以唯一的解决办法就是行动起来&#xff0c;认真做完…

ES7210高性能四通道音频ADC转换模拟麦克风为IIS数字咪头

特征 高性能多位 Delta-Σ 音频 ADC 102 dB 信噪比 -85 分贝 THDN 24 位&#xff0c;8 至 100 kHz 采样频率 I2S/PCM 主串行数据端口或从串行数据端口 支持TDM 256/384Fs、USB 12/24 MHz 和其他非标准音频系统时钟 低功耗待机模式 应用 麦克风阵列 智能音箱 远场语音捕获 订购…

npm安装完yarn还是用不了?

前言 解决 找到你的包全局安装目录 复制路径&#xff0c;配置到Path全局环境变量 结果 不过发现在idea里还是用不了&#xff0c;此时你会想&#xff0c;这什么烂贴&#xff0c;没一点屁用 不过在重启idea之后&#xff0c;你也许就不会这么想了

【网络安全】实验五(身份隐藏与ARP欺骗)

一、本次实验的实验目的 &#xff08;1&#xff09;了解网络攻击中常用的身份隐藏技术&#xff0c;掌握代理服务器的配置及使用方法 &#xff08;2&#xff09;通过实现ARP欺骗攻击&#xff0c;了解黑客利用协议缺陷进行网络攻击的一般方法 二、搭配环境 打开三台虚拟机&#…

本地多卡(3090)部署通义千问Qwen2-72B大模型提速实践:从龟速到够用

最近在做文本风格转化&#xff0c;涉及千万token级别的文本。想用大模型转写&#xff0c;在线的模型一来涉及数据隐私&#xff0c;二来又不想先垫钱再找报销。本地的7-9B小模型又感觉效果有限&#xff0c;正好实验室给俺配了4卡3090的机子&#xff0c;反正也就是做个推理&#…

掌握MySQL基础命令:数据表结构修改详细操作

MySQL数据表&#xff08;Table&#xff09;是MySQL数据库中存储数据的基本结构单元。简单来说&#xff0c;数据表可以被看作是一个二维的、由行&#xff08;Row&#xff09;和列&#xff08;Column&#xff09;组成的表格&#xff0c;其中每一行代表了一个记录&#xff08;Reco…

微服务的分布式事务解决方案

微服务的分布式事务解决方案 1、分布式事务的理论模型1.1、X/Open 分布式事务模型1.2、两阶段提交协议1.3、三阶段提交协议 2、分布式事务常见解决方案2.1、TCC补偿型方案2.2、基于可靠性消息的最终一致性方案2.3、最大努力通知型方案 3、分布式事务中间件 Seata3.1、AT 模式3.…