【C语言】编译与链接

1.翻译环境与运行环境

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

1.翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)

2.执行环境,它用于实际执行代码

 

 2.翻译环境

那么翻译环境是怎么将源代码转换为可执行的机器指令的呢?这里我们就得展开讲解一下翻译环境所做的事情。

其实翻译环境由编译链接两个大的过程组成,而编译又可以分解成三个部分:预处理(也叫预编译)、编译、汇编。

一个C语言的项目中可能由多个.c文件一起构建

多个.c文件生成可执行程序又可分为一下几个步骤:
多个. c文件单独经过编译器,编译处理生成对应的目标文件(在windows环境下目标文件的后缀是.obj,Linux环境下目标文件的后缀是.o)。

多个目标文件和链接库一起经过链接处理生成最终的可执行程序(链接库是指运行库(它是支持程序运行的基本函数集合)或者第三方库)。

如果再把编译器展开成三个过程,那就变成了下面的过程:

2.1 预处理(预编译)

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

gcc环境下观察一下,对test.c文件预处理后的.i文件,命令如下:

gcc  -E  test.c  -o test.i

预处理阶段主要处理那些源文件中#开始的预处理指令,比如:#include,#define,处理的规则如下:

将所有的#define删除,并展开所有的宏定义。

处理所有的条件编译指令,如#if,#iddef,#elif,#endif

处理#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。

删除所有的注释(也就是你对代码的解释,即//后面的文字)

比如:

test.c中的代码为:

#include<stdio.h>
int main()
{printf("hello world");//打印hello worlereturn 0;
}

在生成的test.i中就会把注释删掉

添加行号和文件名标识,方便后续编译器生成调试信息等。

或保留所有的#pragma的编译器指令,编译器后续会使用。

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

 2.2 编译

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

编译过程的命令如下:

gcc -S test.i -o test.s

 那么对下面代码进行编译的时候,会这么做呢?

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

首先对其进行词法分析 

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

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

接下来进行语法分析:

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

最后进行语义分析:

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

(如果想要进一步了解编译过程,可以去看看《编译原理》这一本书) 

2.3 汇编

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

汇编的命令如下:

gcc -c test.s -o test.o

2.4 链接

链接是一个复杂的过程,链接的时候需要把一堆文件链接在一起才可以生成可执行程序。

链接过程主要包括:地址和空间分配,符号决议和重定义等这些步骤。

链接解决的是一个项目多文件、多模块之间相互调用的问题。

比如:

在一个c项目中有两个.c文件(test.c和add.c),代码如下:
test.c:

#include <stdio.h>
//test.c
//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;
int main()
{int a = 10;int b = 20;int sum = Add(a, b);printf("%d\n", sum);return 0;
}

add.c:

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

 我们已经知道,每个源文件都是单独经过编译器处理形成对应的目标文件。

test.c经过编译处理生成test.o

add.c经过编译处理生成add.o

我们在test.c文件中使用了add.c文件中的Add函数和g_val变量。

我们在test.c文件每一次使用Add函数和g_val的时候必须确切的知道Add和g_val的地址,但是由于每个文件是单独编译的,在编译器编译test.c的时候并不知道Add函数和g_val变量的地址,所以暂时把Add的指令的目标地址和g_val的地址搁置。等待最后链接的时候由编译器等根据引用的符号Add在其他模块查找Add函数的地址,然后将test.c中所有引用到Add的指令重新修正,让他们的目标地址成为真正的Add函数的地址,对于全局变量g_val也是类似的方法来修正地址。这个地址修正的过程也被叫做:重定位。

3. 运行环境

1.程序必须载入内存中。在由操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。

2.程序开始执行代码。接着使用main函数。

3.开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数局部变量和返回值地址。程序同时也可以使用静态(static)内存,存储于静态变量在程序的整个执行过程一直保留他们的值。

4.终止程序。正常终止main函数,也有可能时意外终止。

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

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

相关文章

Windows系统中下Oracle 19C数据库超级详细安装、设置教程(自己电脑上安装Oracle学习,保姆级教学,亲测有效)

Oracle 官方提供了一个基于 Java 技术的图形界面安装工具&#xff1a;Oracle Universal Installer&#xff08;Oracle 通用安装器&#xff09;简称 OUI&#xff0c;利用它可以完成在不同操作系统平台上&#xff08;Windows、Linux、UNIX&#xff09;的、不同类型的、不同版本的…

历时三年,花了200万,小米换的新标值这个价吗?

原创 航通社 航通社 收录于话题#小米1#小米新logo1#营销1 以及为什么要搞一套“设计哲学” 航通社首发原创文章&#xff0c;未经授权禁止转载 微博&#xff1a;航通社 | 微信搜一搜&#xff1a;2021年 第12期 文 / 书航 2021.3.31 小米集团宣布了 10 年来的首次标识&#x…

uni-app canvas 签名

调用方法 import Signature from "/components/signature.vue" const base64Img ref() //监听getSignImg uni.$on(getSignImg, ({ base64, path }) > {base64Img.value base64//console.log(签名base64, path >, base64, path) //拿到的图片数据// 之后取消…

Levenberg-Marquardt (LM) 算法进行非线性拟合

目录 1. LM算法2. 调包实现3. LM算法实现4. 源码地址 1. LM算法 LM算法是一种非线性最小二乘优化算法&#xff0c;用于求解非线性最小化问题。LM主要用于解决具有误差函数的非线性最小二乘问题&#xff0c;其中误差函数是参数的非线性函数&#xff0c;需要通过调整参数使误差函…

Vue Canvas图片水印的绘制 图片加水印

效果 定义画布 <canvas width"800" height"800" ref"cn" ></canvas>绘制水印 draw(){const img new Image()img.srchttps://img1.baidu.com/it/u3035183739,1826404114&fm253&fmtauto&app138&fJPEGimg.onload(()…

pyqt 动态更换表头和数据

目录 pyqt 动态更换表头和数据代码 效果图&#xff1a; pyqt 动态更换表头和数据代码 from PyQt5.QtGui import QColor, QBrush from PyQt5.QtWidgets import QApplication, QTableWidget, QVBoxLayout, QWidget, QPushButton, QTableWidgetItemclass Example(QWidget):def _…

Redis底层数据结构之ZSkipList

目录 一、概述二、ZSkipList结构三、和平衡树和哈希表的对比 redis底层数据结构已完结&#x1f44f;&#x1f44f;&#x1f44f;&#xff1a; ☑️redis底层数据结构之SDS☑️redis底层数据结构之ziplist☑️redis底层数据结构之quicklist☑️redis底层数据结构之Dict☑️redis…

机器学习和深度学习-- 李宏毅(笔记与个人理解)Day22

Day 22 Transformer seqence to seqence 有什么用呢&#xff1f; Encoder how Block work 仔细讲讲Residual 的过程&#xff1f; 重构 Decoder - AutoRegressive Mask 由于是文字接龙&#xff0c;所以无法考虑右边的 info 另一种decoder Encoder to Decoder – Cross Attend…

llama3本地部署

目录 II.下载 II.验证ollama安装 II.安装llama3 和启动 II.命令行调用 II.api调用 II.参考文献 II.下载 https://ollama.com/download/windows OllamaSetup.exe https://github.com/meta-llama/llama3 II.验证ollama安装 cmd ollama II.安装llama3 和启动 ollama run …

【问题分析】TaskDisplayArea被隐藏导致的黑屏以及无焦点窗口问题【Android 14】

1 问题描述 用户操作出的偶现的黑屏以及无焦点窗口问题。 直接原因是&#xff0c;TaskDisplayArea被添加了eLayerHidden标志位&#xff0c;导致所有App的窗口不可见&#xff0c;从而出现黑屏和无焦点窗口问题&#xff0c;相关log为&#xff1a; 这个log是MTK添加的&#xff0…

Django模型继承之多表继承

在Django模型继承中&#xff0c;支持的第二种模型继承方式是层次结构中的每个模型都是一个单独的模型。每个模型都指向分离的数据表&#xff0c;并且可以被独立查询和创建。在继承关系中&#xff0c;子类和父类之间通过一个自动创建的OneToOneField进行连接。示例代码如下&…

C语言入门课程学习笔记-6

C语言入门课程学习笔记-6 第27课 - 字符数组与字符串&#xff08;上&#xff09;第28课 - 字符数组与字符串&#xff08;下&#xff09;第29课 - 数组专题练习&#xff08;上&#xff09;第30课 - 数组专题练习&#xff08;下&#xff09; 本文学习自狄泰软件学院 唐佐林老师的…

不只有 Spring,这四款Java 基础开发框架同样值得关注!

Java 开发不只有 Spring &#xff0c;今天给大家推荐几个同样优秀的 Java 基础开发框架&#xff0c;为日常项目开发提供更多的选择。答应我&#xff0c;请不要再叫我 Spring 小子了&#xff0c;​好吗&#xff1f; 项目概览&#xff1a; Guice&#xff1a;轻量级依赖注入框架 …

2024Mac系统热门游戏排行榜 Mac支持的网络游戏有哪些?mac能玩哪些大型网游 苹果电脑Mac游戏资源推荐 Mac玩Windows游戏

“游戏是这个世界上唯一能和女性争夺男朋友的东西&#xff08;/滑稽&#xff0c;有不少女生也喜欢玩游戏&#xff09;。” 虽然只是一句玩笑话&#xff0c;不过也可以看出游戏对大多数男生来说是必不可少的一项娱乐活动了。而网络游戏是游戏中的一大分支&#xff0c;能让玩家们…

科技“冷”战:NIST刷新制冷效率,中国实力逆境崛起!

4月23日&#xff0c;美国国家标准与技术研究院&#xff08;NIST&#xff09;的研究人员报道称&#xff0c;他们通过对常用于科研和工业领域的制冷机进行改装&#xff0c;显著降低了将材料冷却至略高于绝对零度所需的时间和能量。 科学家们指出&#xff0c;他们的原型设备每年能…

Linux 学习之路 -- 进程篇 -- 进程控制

目录 一、进程终止 <1>使用语言和系统自带的方法&#xff0c;进行转换 <2>自定义错误码 <3>小结&#xff1a; <2>两个接口exit / _exit 二、进程等待 <1>简单了解 <2>wait调用 <3>waitpid调用 <4>status <1>W…

复杂的字符串算法——KMP算法

字符串算法 模式匹配&#xff08;Pattern Matching&#xff09;&#xff1a;在一篇长度为 &#x1d45b; 的文本 &#x1d446; 中&#xff0c;找某个长度为 &#x1d45a; 的关键词 &#x1d443;。&#x1d443; 可能多次出现&#xff0c;都需要找到。 最优的模式匹配算法复…

AHB传输---突发操作

突发操作 在本协议中定义了4拍、8拍和16拍的突发&#xff0c;以及未定义长度的突发和单次传输。它支持增量和包装突发&#xff1a; 增量突发访问连续位置&#xff0c;每个传输的地址是前一个地址的增量。包装突发在跨越地址边界时会包装。地址边界的计算方法是突发中拍数与传…

Android—统一依赖版本管理

依赖版本管理有多种方式 config.gradle 用于Groovy DSL&#xff0c;新建一个 config.gradle 文件&#xff0c;然后将项目中所有依赖写在里面&#xff0c;更新只需修改 config.gradle文件内容&#xff0c;作用于所有module buildSrc 可用于Kotlin DSL或Groovy DSL&#xff0c;…

MATLAB冒号表示法

MATLAB 冒号表示法 colon(:)是在MATLAB中最有用的运算符之一。它用于创建向量&#xff0c;下标数组和指定迭代。 如果要创建包含1到10的整数的行向量&#xff0c;请编写- 示例 1:10 MATLAB执行该语句并返回包含1到10的整数的行向量- ans 1 2 3 4 5 6 7 8 9 10 如果要指定一…