深入详解编译与链接:翻译环境和运行环境,翻译环境:预编译+编译+汇编+链接,运行环境

news2025/6/4 22:44:57

目录

一、翻译环境和运行环境

二、翻译环境:预编译+编译+汇编+链接

(一)预处理(预编译)

(二)编译

1、词法分析

2、语法分析

3、语义分析

(三)汇编

(四)链接

三、运行环境

结尾


🔥个人主页:艾莉丝努力练剑

🍓专栏传送门:《C语言》

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平  



前言:前面几篇文章介绍了c语言的一些知识,包括循环、数组、函数、VS实用调试技巧、函数递归、操作符、指针、字符函数和字符串函数、C语言内存函数、数据在内存中的存储、结构体、联合和枚举、动态内存管理、文件操作等,在这篇文章中,我将开始介绍编译和链接的一些重要知识点!对编译和链接感兴趣的友友们可以在评论区一起交流学习!   


一、翻译环境和运行环境

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

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

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

二、翻译环境:预编译+编译+汇编+链接

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

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

1、多个.c文件单独经过编译器,编译处理生成对应的目标文件;

2、注意:在Windows环境下的目标文件的后缀是.obj,Linux环境下目标文件的后缀是.o;

3、多个目标文件和链接一起经过链接器处理生成最终的可执行程序;

4、链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库。

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

我们这里以gcc为例,展现拆解编译链接的过程。 

(一)预处理(预编译)

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

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

gcc -E test.c -o test.i

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

1、将所有的#define删除,并且展开所有的宏定义;

2、处理所有的条件编译指令,比如:#if、#ifder、#elif、#else、#endif。

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

4、删除所有的注释;

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

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

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

(二)编译

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

编译过程的命令如下:

gcc - S test.i - o test.s

对下面代码进行编译的时候, 我们会怎么做呢?代码如下:

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

我们程序员肯定一眼就能看出来这是一个C语言代码,但是不够啊,这只是你看懂了,你得让计算机看懂啊,得让编译器看懂啊。编译器会怎么处理呢?这个时候就要编译,

1、词法分析

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

上面的程序进行语法分析之后得到了16个记号,即:

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

接下来语法分析器将对扫描产生的记号进行语法分析,从而产生语法树,这些语法树是以表达式为节点的树。如下图,上面的程序可以这样表示:

3、语义分析

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

预处理和编译部分就如下图:

(三)汇编

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

汇编的命令如下:

gcc - c test.s - o test.o

(四)链接

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

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

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

打个比方,在一个C的项目中有两个.c文件(分别是test.c和add.c),代码如下所示:

(1)test.c

#define  _CRT_SECURE_NO_WARNINGS  1

#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("sum=%d\n", sum);

	printf("g_val=%d", g_val);
	return 0;
}

(2)add.c

#define  _CRT_SECURE_NO_WARNINGS  1


//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也是同理,用类似的方法来修正地址。

这个地址修正的过程也被称为:重定位。 

前面我们简单地讲解了一个C的程序是如何编译和链接到最终生成可执行程序的过程,其实很多内部的细节我们在这里没办法详细展开介绍了。如:目标文件的格式elf,链接底层实现中的空间与地址分配,符号解析和重定位等等。

这里给对其内部细节感兴趣的友友们推荐一本书:

《程序员的自我修养》——可以通过这本书来详细了解,这里我们就不多赘言啦。

三、运行环境

要求:

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

2、程序的执行便开始。接着就调用main函数;

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

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


结尾

注:上面的代码测试我们可以在VS Code(即“Visual Studio Code”)进行,在新版本的VS上可能观察的不那么明显,大家可以安装一下VS Code,安装教程本站也可以搜到。


往期回顾:

【掌握文件操作】(下):文件的顺序读写、文件的随机读写、文件读取结束的判定、文件缓冲区

【掌握文件操作】(上):二进制文件和文本文件、文件的打开和关闭、文件的顺序读写

【动态内存管理】深入详解:malloc和free、calloc和realloc、常见的动态内存的错误、柔性数组、总结C/C++中程序内存区域划分

【详解自定义类型:联合和枚举】:联合体类型的声明、特点、大小的计算,枚举类型的声明、优点和使用

【自定义类型:结构体】:类型声明、结构体变量的创建与初始化、内存对齐、传参、位段

结语:本篇文章就到此结束了,本文为友友们分享了编译和链接相关的一些重要知识点,如果友友们有补充的话欢迎在评论区留言,下一篇博客,我们将介绍预处理相关的内容,敬请期待,感谢友友们的关注与支持!  

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

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

相关文章

定时任务:springboot集成xxl-job-core(二)

定时任务实现方式&#xff1a; 存在的问题&#xff1a; xxl-job的原理&#xff1a; 可以根据服务器的个数进行动态分片&#xff0c;每台服务器分到的处理数据是不一样的。 1. 多台机器动态注册 多台机器同时配置了调度器xxl-job-admin之后&#xff0c;执行器那里会有多个注…

DeviceNET转EtherCAT网关:医院药房自动化的智能升级神经中枢

在现代医院药房自动化系统中&#xff0c;高效、精准、可靠的设备通信是保障患者用药安全与效率的核心。当面临既有支持DeviceNET协议的传感器、执行器&#xff08;如药盒状态传感器、机械臂限位开关&#xff09;需接入先进EtherCAT高速实时网络时&#xff0c;JH-DVN-ECT疆鸿智能…

一:UML类图

一、类的设计 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 学习设计模式的第一步是看懂UML类图,类图能直观的表达类、对象之间的关系,这将有助于后续对代码的编写。 类图在软件设计及应用框架前期设计中是不可缺少的一部分,它的主要成分包括:类名、…

Java 中 MySQL 索引深度解析:面试核心知识点与实战

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Java 中 MySQL 索引深度解析&#xff1a;面试…

设计模式之结构型:装饰器模式

装饰器模式(Decorator Pattern) 定义 装饰器模式是一种​​结构型设计模式​​&#xff0c;允许​​动态地为对象添加新功能​​&#xff0c;而无需修改其原始类。它通过将对象包装在装饰器类中&#xff0c;以​​组合代替继承​​&#xff0c;实现功能的灵活扩展(如 Java I/O …

MySQL安装及启用详细教程(Windows版)

MySQL安装及启用详细教程&#xff08;Windows版&#xff09; &#x1f4cb; 概述 本文档将详细介绍MySQL数据库在Windows系统下的下载、安装、配置和启用过程。 &#x1f4e5; MySQL下载 官方下载地址 官方网站: https://dev.mysql.com/downloads/社区版本: https://dev.my…

【HarmonyOS Next之旅】DevEco Studio使用指南(二十九) -> 开发云数据库

目录 1 -> 开发流程 2 -> 创建对象类型 3 -> 添加数据条目 3.1 -> 手动创建数据条目文件 3.2 -> 自动生成数据条目文件 4 -> 部署云数据库 1 -> 开发流程 云数据库是一款端云协同的数据库产品&#xff0c;提供端云数据的协同管理、统一的数据模型和…

批量导出CAD属性块信息生成到excel——CAD C#二次开发(插件实现)

本插件可实现批量导出文件夹内大量dwg文件的指定块名的属性信息到excel&#xff0c;效果如下&#xff1a; 插件界面&#xff1a; dll插件如下&#xff1a; 使用方法&#xff1a; 1、获取此dll插件。 2、cad命令行输入netload &#xff0c;加载此dll&#xff08;要求AutoCAD&…

Goreplay最新版本的安装和简单使用

一&#xff1a;概述 Gor 是一个开源工具&#xff0c;用于捕获实时 HTTP 流量并将其重放到测试环境中&#xff0c;以便使用真实数据持续测试您的系统。它可用于提高对代码部署、配置更改和基础设施更改的信心。简单易用。 项目地址&#xff1a;buger/goreplay: GoReplay is an …

Android Studio 解决报错 not support JCEF 记录

问题&#xff1a;Android Studio 安装Markdown插件后&#xff0c;报错not support JCEF不能预览markdown文件。 原因&#xff1a;Android Studio不是新装&#xff0c;之前没留意IDE自带的版本是不支持JCEF的。 解决办法&#xff1a; 在菜单栏选中Help→Find Action&#xff…

sigmastar实现SD卡升级

参考文章:http://wx.comake.online/doc/DD22dk2f3zx-SSD21X-SSD22X/customer/development/software/Px/zh/sys/P3/usb%20&%20sd%20update.html#21-sd 1、构建SD卡升级包 在project下make image完成后使用make_sd_upgrade_sigmastar.sh脚本打包SD卡升级包。 ./make_sd_up…

kafka学习笔记(三、消费者Consumer使用教程——配置参数大全及性能调优)

本章主要介绍kafka consumer的配置参数及性能调优的点&#xff0c;其kafka的从零开始的安装到生产者&#xff0c;消费者的详解介绍、源码及分析及原理解析请到博主kafka专栏 。 1.消费者Consumer配置参数 配置参数默认值含义bootstrap.servers无&#xff08;必填&#xff09;…

【论文笔记】Transcoders Find Interpretable LLM Feature Circuits

Abstract 机制可解释性(mechanistic interpretability)的核心目标是路径分析(circuit analysis)&#xff1a;在模型中找出与特定行为或能力对应的稀疏子图。 然而&#xff0c;MLP 子层使得在基于 Transformer 的语言模型中进行细粒度的路径分析变得困难。具体而言&#xff0c;…

每天总结一个html标签——a标签

文章目录 一、定义与使用说明二、支持的属性三、支持的事件四、默认样式五、常见用法1. 文本链接2. 图片链接3. 导航栏 在前端开发中&#xff0c;a标签&#xff08;锚点标签&#xff09;是最常用的HTML标签之一&#xff0c;主要用于创建超链接&#xff0c;实现页面间的跳转或下…

android binder(1)基本原理

一、IPC 进程间通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;机制&#xff0c;用于解决不同进程间的数据交互问题。 不同进程之间用户地址空间的变量和函数是不能相互访问的&#xff0c;但是不同进程的内核地址空间是相同和共享的&#xff0c;我们可…

行业分析---小米汽车2025第一季度财报

1 背景 最近几年是新能源汽车的淘汰赛&#xff0c;前短时间比亚迪再次开始了降价&#xff0c;导致一片上市车企的股价大跌&#xff0c;足见车圈现在的敏感度。因此笔者会一直跟踪新势力车企的财报状况&#xff0c;对之前财报分析感兴趣的读者朋友可以参考以下博客&#xff1a;…

边缘计算网关支撑医院供暖系统高效运维的本地化计算与边缘决策

一、项目背景 医院作为人员密集的特殊场所&#xff0c;对供暖系统的稳定性和高效性有着极高的要求。其供暖换热站传统的人工现场监控方式存在诸多弊端&#xff0c;如人员值守成本高、数据记录不及时不准确、故障发现和处理滞后、能耗难以有效监测和控制等&#xff0c;难以满足…

简单了解string类的特性及使用(C++)

string的特性 string类不属于STL&#xff0c;它属于标准库 但由于它具有数据结构的特性&#xff0c;所以从归类的角度&#xff0c;可以将string类归类到容器里面去 在C标准库中&#xff0c;std::string 是一个特化的类型&#xff0c;实际上是 std::basic_string 的别名。std…

FastAPI+Pyomo实现线性回归解决饮食问题

之前在 FastAPI介绍-CSDN博客 中介绍过FastAPI&#xff0c;在 Pyomo中线性规划接口的使用-CSDN博客 中使用Pyomo解决饮食问题&#xff0c;这里将两者组合&#xff0c;即FastAPI在服务器端启动&#xff0c;通过Pyomo实现线性回归&#xff1b;客户端通过浏览器获取饮食的最优解。…

16.FreeRTOS

目录 第1章 FreeRTOS 实时操作系统 1.1 认识实时操作系统 1.1.1 裸机的概念 1.1.2 操作系统的概念 1.2 操作系统的分类 1.3 常见的操作系统 1.4 认识实时操作系统 1.4.1 可剥夺型内核与不可剥夺型内核 1.4.2 嵌入式操作系统的作用 1.4.3 嵌入式操作系统的发展 1.4.4…