C语言笔记第16篇:编译和链接

1、翻译环境和运行环境

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

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

第2种是执行环境,它用于实际执行代码

2、翻译环境

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

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

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

  • 多个.c文件单独经过编译处理生产对应的目标文件(.obj)
  • 注:在Windows环境下的目标文件的后缀是.obj,Linux环境下目标文件的后缀是.o
  • 多个目标文件(.obj)和链接库一起经过链接器的处理生成最终的可执行程序
  • 链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库

什么是链接库?

在学习C语言时我们经常会用到库函数,比如printf、scanf,这些函数肯定不是凭空出现的,像这样的库函数是被编译成一个一个的链接库,这些函数都包含在这个链接库中也就是第三方库,是C编译器厂商自己提供的库来供我们使用。在我们的C程序中会用到库函数,但是必须经过链接器目标文件和链接库一起链接才能调用该库函数。

举个例子:比如有一条河,两个人想见面,一个人(程序)在河的这边,而另一个人(库函数实现)在河的那边,那两人想见面(调用)是不是必须搭一座桥,而这座桥就是链接器。

其实还可以把编译器的编译展开成3个过程,那就变成了下面的过程:

Linux环境下:

2.1 预处理(预编译)

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

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

gcc -E test.c -o test.i

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

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

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

2.2 编译

编译过程就是将预处理后的文件进行一系列:词法分析语法分析语义分析、符号汇总(链接阶段会讲一下符号汇总有什么用)及优化,生成相应的汇编指令。简单来说编译过程就是将C语言代码转换成汇编代码。

编译过程的命令如下:

gcc -S test.i -o test.s

对下面代码进行编译的时候,会怎么做呢?假设有下面的代码:

array[index] = (index + 4) * (2 + 6);
2.2.1 词法分析

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

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

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

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

2.2.3 语义分析

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

以上步骤完成后,最后就是将代码生成汇编指令,然后编译阶段就完成了。

2.3 汇编

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

注:这个过程还会形成符号表,是根据编译过程的符号汇总生成符号表的。

汇编的命令如下:

gcc -c test.s -o test.o

2.4 链接

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

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

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

链接主要就是处理不同文件之间的相互调用,比如:

add.c

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

test.c

extern int Add(int x,int y);
extern int g_val;
int main()
{printf("%d\n",g_val);printf("%d\n",Add(2, 3));return 0;
}

这两个文件直接是如何链接的才可以相互调用的呢?

注意:这两个文件会生产目标文件:add.obj、test.obj,在生产目标文件之前的编译过程中会对两个文件进行符号汇总,然后在汇编过程中又会形成符号表。比如add.c文件在编译过程中会进行符号汇总:g_val、Add,test.c在编译过程中进行符号汇总:g_val、Add、main,下一步在汇编过程中每个文件汇总出的符号是会形成符号表的,符号表中每个符号都有对应的地址。

例如:add.obj符号表

符号地址
g_val0x100
Add0x200

test.obj符号表

符号地址
Add0x000(无效的地址)
g_val0x000(无效的地址)
main0x300

注:以上地址是自己填上去的,真正的地址不是这样,只是举个例子使用

test.c里的符号Add和g_val由于是外部声明符号,并不知道符号真实地址,所以形成符号表时就给个无效地址。

链接过程中这些符号表是要进行合并的,多个目标文件都是一个项目的,没必要那么多符号表,所以只需将多个文件的符号表合成一个就够了。

add.obj和test.obj经过链接合并成的符号表:

符号地址
Add0x200
g_val0x100
main0x300

因为合并时找到了符号本身的有效地址,多以合并时将无效地址替换掉了,最终两个文件的符号表合并在了一起,运行时便可以通过该符号表的地址找到对应符号并调用。

而合并符号表过程中将test.obj符号表中Add符号的无效地址或g_val符号的无效地址替换掉就叫做符号的决议和重定位

总结:

多个文件之间相互调用首先需要在编译阶段进行符号汇总,然后汇编阶段将汇总出的符号形成符号表,符号表中的每个符号都分配有对应地址。最后在链接阶段将多个目标文件的符号表进行符号表合并,至此多个文件的符号都有了联系,一个文件如果想调用另一个文件的函数就可以通过符号表的地址找到该函数并调用。

3、运行环境

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

2. 程序的指向便开始,接着便调用main函数。

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

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

本篇C语言笔记第16篇:编译和链接 到了这里也就结束了,我们下一篇笔记再见-

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

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

相关文章

数据资产管理的未来趋势:洞察技术前沿,探讨数据资产管理在云计算、大数据、区块链等新技术下的发展趋势

一、引言 随着信息技术的飞速发展,数据已成为企业最重要的资产之一。数据资产管理作为企业核心竞争力的关键组成部分,其发展趋势和技术创新受到了广泛关注。特别是在云计算、大数据、区块链等新技术不断涌现的背景下,数据资产管理面临着前所…

通过文章id递归查询所有评论(xml)

<!-- 通过文章id递归查询所有评论 --> <select id"findByArticleId" resultMap"commentResultMap">SELECT * FROM mxg_comment WHERE parent_id -1AND article_id #{articleId}ORDER BY create_date DESC </select><!-- 将每…

【凤凰房产-注册安全分析报告-缺少轨迹的滑动条】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

2024 端午节巽寮湾游玩记录

2024 端午节巽寮湾游玩记录 文章目录 2024 端午节巽寮湾游玩记录一、前言二、巽寮湾游玩行程1、三天衣食住行2、主要图片&#xff1a; 三、其他1、小结2、巽寮湾游玩建议3、感慨 一、前言 时间总是过得很快&#xff0c;只要你活着时间就会不停往前走。 所以你以后的路其实都是…

外卖APP开发详解:从同城O2O系统源码开始

近期&#xff0c;从事软件开发的小伙伴们都在讨论外卖APP&#xff0c;热度非常之高&#xff0c;所以小编今天将与大家一同探讨同城O2O系统源码、外卖APP开发。 一、外卖APP开发的前期准备 了解目标用户的需求&#xff0c;分析竞争对手的优劣势&#xff0c;明确自身的市场定位。…

如何在Vue3中处理异步API调用并更新表单数据(附Demo)

目录 前言1. 问题所示2. 知识分析3. 实战 前言 从实战问题中剖析知识点 1. 问题所示 执行Vue3数据的时候&#xff0c;终端输出的data如下所示 Promise {<pending>} [[Prototype]] : Promise [[PromiseState]] : "fulfilled" [[PromiseResult]] : Array(…

Paddleocr数据增强调用逻辑

数据增强调用逻辑 以在ppocr/data/simple_dataset.py为例&#xff1a; get_ext_data通过self.ops[:self.ext_op_transform_idx]获取配置文件中数据增强 self.ops在def __init__(self, config, mode, logger, seedNone):中通过解析配置文件中transforms内容获取数据增强操作&a…

ADB->获取当前正在显示的Fragment和Activity的ADB命令

获取当前显示的Activity adb shell "dumpsys window | grep mCurrentFocus"指令拆解adb shell&#xff1a;启动一个远程shell来运行设备上的命令dumpsys window&#xff1a;获取当前窗口管理器的信息|&#xff1a;将前一个命令的输出作为后一个命令的输入grep mCurr…

【Unity拖拽物体】实现对点中的3D物体进行拖拽的功能

场景结构&#xff0c;两个普通模型 第一种 脚本所挂载的物体才可以被拖拽 【PC鼠标版本】 using UnityEngine;// 这个脚本实现了&#xff0c;本脚本所在的游戏物体能够被拖拽 public class DragObjectT : MonoBehaviour {private Vector3 screenPoint; // 存储物体在屏幕上的位…

python GUI开发: tkinter菜单创建,记事本和画图软件综合项目的实战演练

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【0287】Postgres内核在pg_internal.init不存在情况下的relcache cache初始化实现

上一篇: 【0264】深入分析relcache(relation descriptor cache)缓存初始化第2阶段(2) 0. 前言 在用户未CREATE DATABASE 情况(即initdb创建了Postgres数据库集簇,或有过psql登录数据库动作)下,PGDATA/global目录下是还不存在pg_internal.init文件的,因此,启动Post…

elasticsearch安装(centos7)

先给出网址 elasticsearch&#xff1a;Download Elasticsearch | Elastic elasticKibana&#xff1a;Download Kibana Free | Get Started Now | Elastic Logstash&#xff1a;Download Logstash Free | Get Started Now | Elastic ik分词&#xff1a;Releases infinilabs/…

百度智能云推出智能运维工具,云助手让云服务器运维更简单

为了提升云服务器执行命令的效率&#xff0c;百度智能云发布了 SmartTerm 远程连接终端。不止于此&#xff0c;为了更加极致地提升运维效率&#xff0c;我们又推出了「云助手」这款轻量快捷的运维工具。 ​ 只有做过云服务器运维的人才知道管理上万台云服务器有多崩溃。在海量…

Ruby 数据库访问 - DBI 教程

Ruby 数据库访问 - DBI 教程 本文将详细介绍如何使用 Ruby 的 DBI(Database Interface)库来访问和操作数据库。DBI 是 Ruby 语言中一个常用的数据库接口库,它提供了一套统一的接口来访问不同的数据库系统,如 MySQL、PostgreSQL、SQLite 等。通过本文的学习,您将掌握如何使…

了解振弦采集仪在建筑物安全监测中的应用与研究

了解振弦采集仪在建筑物安全监测中的应用与研究 摘要&#xff1a;河北稳控科技振弦采集仪是一种常用的结构物安全监测设备&#xff0c;广泛应用于建筑物、桥梁、塔楼等工程结构的监测。本文将从振弦采集仪的原理、应用案例和研究进展等方面进行详细介绍&#xff0c;以便更好地…

解决python从TD数据库取50w以上大量数据慢的问题

1.问题背景描述 python项目中的时序数据都存放在TD数据库中&#xff0c;数据是秒级存入的&#xff0c;当查询一周数据时将超过50w数据量&#xff0c;这是一次性获取全量数据到python程序很慢&#xff0c;全流程10秒以上&#xff0c;希望进行优化加速 2.排查 首先&#xff0c…

vue中使用发布订阅的方式进行vue组件之间的通信

全部实现代码如下&#xff1a; header.vue组件的相关代码 Search GitHub Users <input type“button” value“Search” class“btn btn-primary” placeholder“请输入 github 用户名” click“search”> main组件的相关代码 请输入搜索用户的名称 loading {{error…

springboot3 基础特性(1)

文章目录 一、SpringApplication三种方式1.1 基础方式1.2.自定义 SpringApplication1.3、FluentBuilder API 二、自定义Banner三、Profiles3.1 什么是 Profiles &#xff1f;3.2 声明Profiles3.3 激活配置文件3.3.1 分组3.3.2 环境包含3.3.3 激活方式3.3.4 配置优先级 一、Spri…

常用告警规则

Node_exporter告警规则 NodeCPUUsageHigh: yaml 复制代码 alert: NodeCPUUsageHigh expr: (100 - (avg by (instance) (rate(node_cpu_seconds_total{mode“idle”}[5m])) * 100)) > 80 for: 5m labels: severity: critical annotations: summary: “High CPU usage detec…

STM32单片机USART串口详解

文章目录 1. 通信接口概述 2. 串口通信 3. 硬件电路 4. 电平标准 5. 串口参数及时序 5.1 数据帧的组成 5.2 起始位 5.3 数据位 5.4 校验位 5.5 停止位 5.6 波特率 5.7 数据帧传输过程示例 6. 串口时序 7. USART概述 8. USART框图 9. USART基本结构 10. 数据帧…