【C语言】编译和链接(编译环境和运行环境)

news2024/10/25 2:25:56

在这里插入图片描述

文章目录

  • 一、翻译环境和运行环境
  • 二、翻译环境
    • 1.编译
      • 预处理
      • 编译
      • 汇编
    • 2.链接
  • 四、运行环境

一、翻译环境和运行环境

   在 ANSI C 的任何⼀种实现中,存在两个不同的环境,如下:

  1. 翻译环境:在翻译环境中,会通过编译和链接两个大步骤,其中编译又分为了预处理(预编译)、编译和汇编,将源代码转换为可执⾏的机器指令(⼆进制指令),生成可执行程序

  2. 运行环境:即执行环境,在运行环境中会执行可执行程序,并输出结果

如下图:
在这里插入图片描述
   接下来我们就来学习在翻译环境和运行环境中具体会做些什么

二、翻译环境

   上面讲到了,翻译环境是用来将源代码转换为可执⾏的机器指令(⼆进制指令),生成可执行程序的,那么它到底是怎么将源代码转换成可执行的机器指令,又是怎么把机器指令生成可执行程序呢?我们一起来学习一下
   翻译环境是由编译和链接两个⼤的过程组成的,⽽编译⼜可以分解成:预处理(有些书也叫预编译)、编译、汇编三个过程,接下来我们就来学习它们

1.编译

编译要完成的就是将我们的源代码转换成可执行的机器指令,如图:
在这里插入图片描述
   编译器就可以实现编译的功能,也就包括了预处理,编译,汇编这三个操作,我们学习编译也就是学习这三个操作的过程
   由于VS是一个高度集成的开发环境,它已经把编译这样的细节隐藏起来了,在VS中,我们只需要按下ctrl+f5,那么VS就会一下就帮我们把编译、链接和执行这三个动作一起完成了,瞬间就可以看到结果
   所以在VS中我们无法看到.c的源文件编译和链接的完整过程,这个时候我们就可以借助其它的编译器,在下文中就是以gcc为例进行整个编译链接的讲解

预处理

   预处理又称预编译,在预处理阶段,后缀为.c的文件将会被处理为.i的文件,如test.c经过预处理后就会变成test.i
   预处理阶段要做的事主要有以下几点:

  1. 将所有的 #define 删除,并展开所有的宏定义,比如使用宏定义了一个常量,我们一般会这样写:
//使用宏定义了一个常量
#define N 100
//使用宏
int arr[N];

   那么经过预处理之后,#define N 100这条语句就会被删除,并且这个宏定义将会被展开,在这里就是将所有N替换成100,如下:

//预处理后,宏定义语句被删除
//展开宏定义,在这里就是将N替换成100
int arr[100];
  1. 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif等等,在下一篇预处理详解我们会讲到,这里简单介绍一下
  2. 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置,比如包含头文件stdio.h,那么就会将头文件stdio.h中的所有内容插入到原位置
       虽然VS不会生成.i的文件,但是我们还是可以看到头文件的内容,首先使用#include包含stdio.h,然后使用ctrl+单击鼠标,就可以看到stdio.h这个头文件的内容,有两千多行的代码,在预处理后,就会全部插入到我们的源文件中来
  3. 经过预处理后,会删除所有的注释,所以我们写了注释才不会影响代码的运行,因为在我们正在编译前,就已经把它删除了
  4. 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等
  5. 保留所有的#pragma的编译器指令,它可以保证我们不重复包含头文件

   经过预处理后的 .i ⽂件中不再包含宏定义,因为宏已经被展开,并且包含的头⽂件都被插⼊到 .i⽂件中,当我们⽆法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的 .i ⽂件来确认
   预处理这部分内容还有许多的知识点需要我们掌握,这里就不展开讲了,在下一篇文章,我们会详细讲解预处理的各种指令

编译

   当我们进行预处理后,就来到了编译阶段,编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件
   我们现在就用一句代码为例,看看词法分析、语法分析、语义分析的大致实现思路,如下:

arr[index] = (index+4)*(2+6);
  1. 词法分析:首先代码就来到了词法分析,这个阶段会将源代码程序输⼊扫描器,扫描器的任务就是简单的进⾏词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)
       上⾯程序进⾏词法分析后得到了16个记号,然后就会生成一个记号表,如下图:
    在这里插入图片描述
       在这个阶段会简单的标记每个记号,然后生成一个记号表,这个符号表在后面链接的地方还会用到,我们到时候会再来说它的另一个作用

  2. 语法分析:来到语法分析阶段后,会将源代码放入语法分析器,将对扫描产⽣的记号进⾏语法分析,从⽽产⽣语法树,这些语法树是以表达式为节点的树,如图:
    在这里插入图片描述
       在这个阶段,我们代码的意思基本上就明确了,相当于在语法分析阶段,会把要组合的记号组合起来,明确这些记号的基本含义
       并且在图上我们也可以看出来,这颗语法树的节点是一个又一个的表达式组成,如果在这个时候出现简单的语法错误就可以发现,比如少写一个括号,就不能像这样构成一个以表达式为节点的语法树,程序就可能会报错

  3. 语义分析:经过词法分析和语法分析后,由语义分析器来完成语义分析,即对表达式的具体语法层⾯的分析
       编译器所能做的分析是语义的静态分析,静态语义分析通常包括声明和类型的匹配,类型的转换等,这个阶段会报告错误的语法信息,如下图:
    在这里插入图片描述
       在语义分析这个阶段就能基本明确程序的语法含义了,如明确了类型、类型的转换等信息,而语法分析中只是对记号表中的记号进行了组合和简单的翻译
       在这个阶段已经可以判断表达式之间的关系了,比如整型加整型是整型,整型赋值给整型等等,并且在这个阶段可以找出语法错误
       比如在赋值表达式的左边算出的结果是一个浮点型,而左边算出来了的却是一个整型,那么就会进行强制类型转换,如果表达式左边是整型,而右边是结构体,就会报错
       而且最关键的一点是,我们通过语义分析已经知道了代码的含义,那么把它翻译成汇编代码也不是难事了,所以在这个阶段会正在将源代码翻译成汇编代码,并做相关的优化

汇编

   经过预处理和编译阶段后就来到了汇编阶段,在编译阶段的语义分析我们就把源代码翻译成了汇编代码,而在汇编阶段做的事情就是将翻译过来的汇编代码再次转换为计算机可以识别的机器指令(二进制指令)
   在翻译时,每⼀个汇编语句⼏乎都对应⼀条机器指令,在翻译期间也不会对代码做什么优化,只是根据汇编指令和机器指令的对照表⼀⼀的进⾏翻译,最终将汇编代码转换成了机器指令,生成一个.obj的目标文件

2.链接

   链接是⼀个复杂的过程,链接的时候需要把⼀堆⽂件链接在⼀起才⽣成可执⾏程序,链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤,那么我们为什么要使用链接这一个步骤呢?
   链接解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题,比如在一个项目中有两个.c文件,代码如下:
   add.c:

//定义一个函数add
int add(int x, int y)
{
	return x + y;
}

   test.c

#include <stdio.h>

int main()
{
	int a = 3;
	int b = 2;
	int ret = add(a, b);
	printf("%d\n", ret);
	return 0;
}

   当我们实现好这两个文件后,直接运行后发现出现了错误,我们来看看具体报错:
在这里插入图片描述
   它说函数add未定义,a是没有声明的标识符,这是为什么呢?这就要涉及到链接了,我们在编译阶段会将我们的源代码翻译成机器指令,生成后缀名为.obj的目标文件,但是我们要注意的是,编译是针对于单个文件的
   什么意思呢?就是一个.c的文件生成一个.obj的目标文件,如果有多个.c的文件则生成多个.obj的文件,它们之间互不影响,所以如果我们想要一个文件中的某个函数在另一个文件中使用就做不到,我们可以画图理解,如图:
在这里插入图片描述
   从图片中我们很明显地看出来了,多个.c文件生成多个.obj文件,之间互不影响,所以如果我们要让一个文件中的某个函数在另一个文件中使用就必须通过链接来完成,接下来我们就一起来学习链接的过程
   在链接过程中,需要用到之前我们在词法分析时生成的符号表,将那些特殊记号记录下来,但是链接时的符号表则更为复杂,会有导出符号表、未解决符号表和地址重定向表三个表,这里我们就简单将它们合并一下,用一个表把类似的原理讲一讲,等后期会出详细的链接过程
   首先我们要知道符号的类型有哪些,如下:

  1. 全局符号(Global symbols):由当前模块定义并能被其他模块引用的符号(指不带static的全局变量)
  2. 外部符号(External symbols):由其他模块定义,并能被当前引用的全局符号
  3. 局部符号(Local symbols):仅由当前模块定义和引用的本地符号。例如,定义的static函数和变量
    接下来我们可以简单的画出add.c中的符号表,在画的时候我们要注意,在add.c中并没有外部符号的引用,出现的符号add拥有正常的声明和定义,所以会直接分配一个虚拟地址,如图:
    在这里插入图片描述

   接下来我们就来画test.c的符号表,为了进行对比,在test.c的符号表中我们也只画出只有add符号的符号表,其它符号就省略掉,如下图:
在这里插入图片描述

   可以对比看出来,在add.c中,add符号被看做全局符号,拥有自己的地址,但是在test.c中,add符号被看做外部符号,只有一段可能错误的地址(不一定就是0地址)
   链接中关键的就是这一步,由于test.c不认识这个符号所以要报错,为了能够正常链接,我们需要做的就是:在test.c中使用extern关键字对add符号进行声明,然后链接器就会知道,这个符号在其它文件中有定义,先给出一个可能错误的地址,之后再修正
   然后最后为了修正test.c中add符号的地址,使它正确,会进行重定位操作,重定位会计算每个定义的符号在虚拟地址空间的绝对地址,将可执行文件中的符号引用处修改为重定位后的地址信息
   将符号表修正后,test.c文件的符号表中的add符号的地址就会修正为正确的地址,test.c文件就可以通过这个地址来访问add函数,这就是链接中的重定位

   前⾯我们⾮常简洁的讲解了⼀个C的程序是如何编译和链接,到最终⽣成可执⾏程序的过程,其实很多内部的细节⽆法展开讲解。⽐如:⽬标⽂件的格式elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看《程序员的⾃我修养》⼀书来详细了解

四、运行环境

   在运行环境中有几个要点,我们只需要简单了解一下:

  1. 程序首先必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序的载⼊必须由⼿⼯安排,也可能是通过可执⾏代码置⼊只读内存来完成
  2. 随后程序开始执行。会直接调⽤main函数
  3. 开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使⽤静态(static)内存,存储于静态内存中的变量在程序的整个执⾏过程⼀直保留他们的值
  4. 终⽌程序。正常终⽌main函数;也有可能是意外终⽌

   今天的编译和链接就讲到这里啦,后面有机会可能还会仔细讲讲链接的过程,这也可能是我们C语言的倒数第二篇博客,下一篇就是C语言的最后一篇了,有没有非常兴奋和有成就感呢?欢迎在评论区留言,有什么问题也欢迎提出
   那么今天就到这里,bye~

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

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

相关文章

鸿蒙软件开发中常见的如何快速自动生成二维码?QRCode组件

QRCode 用于显示单个二维码的组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 二维码组件的像素点数量与内容有关&#xff0c;当组件尺寸过小时&#xff0c;可能出现无法展示内容的情况&…

在 Controller 层对系统作防御性编程

简介 Web 开发中无论是 MVC 还是 DDD 架构 Controller 层都是系统的门面&#xff0c;既对外的接口&#xff0c;对内的接口&#xff0c;一般情况下任何错误必须组织在 Controller 层 如何作 在 Controller 层中的接口使用 try-catch Slf4j RestController("/") Re…

MobileNetV2实现实时口罩检测tensorflow

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【Informer模型复现项目实战】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【…

[LeetCode] 230. 二叉搜索树中第K小的元素

题目描述&#xff1a; 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 小的元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;1示例 2&am…

《计算机视觉》—— 基于 dlib 库的方法将两张人脸图片进行换脸

声明&#xff1a;此篇文章所用的明星照片只为用于演示代码的效果&#xff0c;无诋毁她人肖像之意 一、案例实现的思想 此案例的核心是基于人脸68个关键点检测模型来实现的&#xff0c;人脸68个关键带点检测后的效果如下&#xff1a; 通过对上图中红色区域的转换&#xff0c;…

项目管理必备:如何快速创建项目交付时间表

“不做准备&#xff0c;就准备失败”。项目的成功很大程度上取决于前期规划的充分性和质量。 项目交付时间表是项目管理中一个重要的工具&#xff0c;通过将项目分解为可管理的区块来组织工作。该方法使管理者可以创建分步的工作路径&#xff0c;并展示项目的宏观视图&#xff…

诺贝尔颁给AI啦,没想到高性能计算CHPC也发力了!

诺贝尔化学奖垂青 AlphaFold 3 2020 年 12 月&#xff0c;AlphaFold 2 发布。相较于之前实验成本高、耗时长、蛋白质结构解析困难的传统方法&#xff0c;AlphaFold 2 可以通过深度学习技术&#xff0c;仅需几小时就能准确地预测蛋白质的三维结构&#xff0c;极大地加速了生物…

基于Multisim的水位测量电路设计与仿真

1.利用LED指示灯显示水位&#xff08;最低水位、1/4、1/2、3/4、最高水位&#xff09;。 2.达到最高水位时&#xff0c;自动报警。

uiautomatorviewer安卓9以上正常使用及问题处理

一、安卓9以上使用uiautomatorviewer问题现象 打开Unexpected error while obtaining UI hierarchy 问题详情 Unexpected error while obtaining UI hierarchy java.lang.reflect.InvocationTargetException 二、问题处理 需要的是替换对应D:\software\android-sdk-windows…

AI自动生成PPT哪个软件好?智能生成PPT不再熬夜做课件

大概这世上&#xff0c;都是职场牛马对“PPT”这三个字母的头痛反应最大吧&#xff01; 是的&#xff0c;就连各个年级段的老师也是很头痛——愁着怎样能在排版整齐的情况下&#xff0c;将必考知识点都呈现在PPT每一张幻灯片页面里...... 近期打听到用人工智能生成ppt课件&am…

域渗透相关内网 或 域AD环境搭建 域环境收集方式方法命令 查看 检测域控主机或者域内主机是否安装杀软方式

目录 域基础知识点知道这些就可以了 域 域的优点以及作用 域控 活动目录 活动目录主要功能 域环境搭建操作步骤: 域环境信息收集 信息收集工具 服务端客户端启动方式 CS操作部分 进行信息收集 信息收集操作 域基础知识点知道这些就可以了 域 域模型就是针对大…

MySQL——数据库

什么是数据库 数据库&#xff08;DB , DataBase&#xff09;概念&#xff1a;数据仓库&#xff0c;软件&#xff0c;安装在操作系统&#xff08;window&#xff0c;linux&#xff0c;max&#xff0c;...&#xff09;之上学习数据库最重要的就是学习SQL语句存储500万以下的数据…

HTML5_标签_各类表格的实现

目录 1. 表格标签 1.1 表格的主要作用 1.2 表格的基本语法 1.3 表头单元格标签 1.4 表格属性 案例分析 先制作表格的结构. 后书写表格属性. 代码示例: 1.5 表格结构标签 1.6 合并单元格 合并单元格方式&#xff1a; 目标单元格&#xff1a;(写合并代码) 合并单元…

蓝桥杯——搜索

搜索 DFS基础回溯 回溯法简介&#xff1a; 回溯法一般使用DFS&#xff08;深度优先搜索&#xff09;实现&#xff0c;DFS是一种遍历或搜索图、树或图像等数据结构的算法&#xff0c;当然这个图、树未必要存储下来&#xff08;隐式处理就是回溯法&#xff09;&#xff0c;常见…

shell脚本语法详解

目录 shell语法基础 指定shell解析器 注释 运行 变量 定义变量 引用变量 清除变量值 从键盘获取值 输入单值 添加输入提示语 读取多值 ​编辑 定义只读变量 环境变量 设置环境变量与查看环境变量 特殊变量 三种引号的作用与区别 小括号与大括号 参数传递 位…

【HuggingFace 如何上传数据集 (2) 】国内网络-稳定上传图片、文本等各种格式的数据

【HuggingFace 下载】diffusers 中的特定模型下载&#xff0c;access token 使用方法总结【HuggingFace 下载中断】Git LFS 如何下载指定文件、单个文件夹&#xff1f;【HuggingFace 如何上传数据集】快速上传图片、文本等各种格式的数据 上文的方法因为是 https 协议&#xf…

CORS预检请求配置流程图 srpingboot和uniapp

首先要会判断预检请求 还是简单请求 简单请求 预检请求 #mermaid-svg-1R9nYRa7P9Pll4AK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1R9nYRa7P9Pll4AK .error-icon{fill:#552222;}#mermaid-svg-1R9nYRa7P9Pll4…

JAVA Maven 的安装与配置

一、下载地址 官方网站&#xff1a;Maven – Download Apache Maven 我这里是3.8.6版本 二、安装步骤 maven安装之前要先安装jdk&#xff0c;请确保你的系统已经安装了jdk环境。 1.将下载好的 Maven 进行解压 apache-maven-3.6.8-bin.zip 2.配置本地仓库:修改 conf/settin…

HDU RSA

翻译成中文后&#xff1a; 思路&#xff1a;由题易得&#xff0c;d * e y * f ( n ) 1 ,且gcd ( e , f ( n ) ) 1,所以用扩展欧几里得求出 d &#xff0c;但要保证 d 是非负的&#xff0c;最有用快速幂求出每个字符即可。 #include<bits/stdc.h> using namespace std;…

【Web前端概述】

HTML 是用来描述网页的一种语言&#xff0c;全称是 Hyper-Text Markup Language&#xff0c;即超文本标记语言。我们浏览网页时看到的文字、按钮、图片、视频等元素&#xff0c;它们都是通过 HTML 书写并通过浏览器来呈现的。 一、HTML简史 1991年10月&#xff1a;一个非正式…