你写的C语言代码被翻译成可执行程序,需要这几步

本篇博客会讲解C语言的灵魂知识点:你写出来的C语言代码究竟是如何让计算机识别并且执行的。C语言是一门计算机语言,可以方便程序员和计算机沟通,但是,计算机只认得二进制,怎么会认得你写的C语言代码是什么意思呢?

确实,计算机只认识二进制,但是你写的C语言代码也会经过一些列的步骤,从而被翻译成计算机可以识别的二进制语言,也就是机器语言。把C语言翻译成机器语言的过程就是翻译过程。经过翻译后,C语言代码就被转化为可执行程序,就可以运行了,这个过程就是运行过程。翻译过程依赖的环境就是翻译环境,运行过程依赖的环境就是运行环境。任何C语言代码,想要被计算机识别,都必须依赖这两个环境。

运行环境很好理解,当C语言被翻译成二进制语言时,就产生了一个可执行程序,当这个程序被加载到内存中后,操作系统就会为其开辟栈空间(存储局部变量等)以及静态空间(存储全局变量和静态变量等),并分配对应的堆空间,用于动态内存管理。这些都是学习C语言时应该掌握的知识点。

本篇文章重点讨论的是翻译环境。翻译环境会涉及到编译器链接器,分别用于编译链接。C语言代码写的文件,后缀一般是.c,我们称这样的文件为源文件。源文件经过编译、链接后,会转换成可执行程序,这个过程就是翻译过程。而编译这个过程中,又分为3个阶段,分别是预编译(也称为预处理)、编译、汇编。

上面的讲解可能有点抽象,我画了一张图方便理解。
图解编译链接过程

注意:编译过程是针对每个单独的源文件。也就是说,假设有3个源文件,test1.c, test2.c, test3.c,那么test1.c会被单独编译,test2.c也会单独编译,同理,test3.c也会单独编译,两两之间互不干扰。

1.编译过程

1.1 预编译

编译过程的第1个小步骤是预编译,主要完成的是文本处理。一个源文件(里面都是C语言代码)经过预编译之后,里面还是C语言代码。预编译主要完成的事情有:

  1. 删除注释。
  2. 头文件包含。
  3. #define的宏的替换。
  4. 条件编译。
  5. ……

这里主要讲解前3点,其他点我会专门写一篇博客,主要讲解预编译过程。

删除注释很好理解,这些注释是给人看的,对于计算机来说并没有用,所以会删掉。

头文件包含,比如写#include <stdio.h>就会被stdio.h这个头文件中的内容替换掉。当然不是一股脑全部替换掉,这里面还涉及到一些条件编译等等,会根据具体情况来处理。

#define的宏也会被替换掉。比如如果有#define NUM 100,那么代码中的所有NUM都会被替换成100。或者,#define MAX(x, y) ((x)>(y)?(x):(y))MAX(3, 5)就会被替换成(3)>(5)?(3):(5),以此类推。

以上的操作都是文本处理,所以操作完后,文件中的代码还是C语言代码。经过预编译之后的文件,Linux下一般后缀是.i

1.2 编译

编译过程的第2个小步骤,也称为编译,注意此编译非彼编译,这里的编译指的是编译过程的其中一个步骤。

一个源文件经过预处理之后,会进行编译。前面说过,预处理之后文件中仍然是C语言代码,而编译会把这些C语言代码翻译成汇编代码。这个过程主要完成下面4件事:

  1. 语法分析。
  2. 词法分析。
  3. 语义分析。
  4. 符号汇总

其中,前3点如果展开会过于复杂,我会专门写一篇博客,讲解编译原理。

这里主要讲解第4点。编译会把代码中出现的全局性质的符号汇总起来,方便进一步处理。比如,有2个文件,分别是Test.c和Add.c。

// Test.c
extern int Add(int, int);
int main()
{
	int a = 10;
	int b = 20;
	int sum = Add(a, b);
	return 0;
}
// Add.c
int Add(int x, int y)
{
	return x + y;
}

那么,Test.c经过预编译,编译之后,会被汇总的符号有Add, main;Add.c经过预编译,编译之后,会被汇总的符号有Add。注意,Add.c中的x, y,Test.c中的a, b, sum等都是一些局部的符号,是不会被汇总的。

在Linux下,编译之后形成的文件一般后缀为.s

1.3 汇编

编译过程的第3个小步骤是汇编。汇编时,会把以上的汇编代码翻译成二进制指令。汇编的过程也很复杂,其中值得讲解的是,这个过程中做了一件很重要的事,形成了符号表

前面提到,编译时进行了符号汇总,那么在汇编的过程中,就会根据汇总的符号,形成符号表。通俗来说,编译器会在这个表中为每一个符号填一个地址,这个地址有可能是有效的,也有可能是无效的。比如还是上面的Test.c和Add.c,他们分别进行了预编译、编译之后,都汇总了一些符号。由于main函数的定义就在Test.c中,所以Test.c的编译过程中就会给main填一个有效的地址(假设是0x100),而Add函数的定义不在Test.c中,所以Test.c的编译过程就会给Add填一个无效的地址(假设是0x000)。同理,Add.c的编译过程会给Add填一个有效的地址(假设是0x200)。这2个文件经过汇编之后分别形成一张符号表。

Test.c的符号表:

mainAdd
0x1000x000

Add.c的符号表:

Add
0x200

汇编结束后形成的文件,在Linux下后缀一般是.o。我们一般称这样的文件为目标可重定向二进制文件,简称目标文件

2.链接过程

预编译、编译、汇编统称为编译过程,都是编译器完成的。编译过程结束后,会由链接器完成链接过程。编译过程只是单独处理每个文件,和编译过程不同,而链接过程会把前面编译过程形成的目标文件和一些库链接起来,是同时处理多个文件。链接过程主要做了下面2件事:

  1. 合并段表。
  2. 符号表的合并和重定向

合并段表也是个很复杂的过程。由于前面形成的目标文件都是elf文件,是有格式的,会分成一个一个的段,而链接时,这些段会被分别合并到一起,最终形成的可执行程序也是elf文件。

这里重点讲解符号表的合并和重定向。前面提到,汇编过程中,每个.o文件都对应形成了一张符号表,那么链接过程就会把这几张表合并为一个新的符号表。合并的规则非常简单,每个符号都需要找到一个唯一对应的有效地址。比如,前面的Test.c和Add.c的例子中,Add符号只有一个有效的地址0x200,main符号只有一个有效的地址0x100。那么合并之后的符号表就是:

mainAdd
0x1000x200

这就能解释一些现象了。如果把Add.c中的Add函数整个删掉,合并符号表时,Add符号就找不到一个有效的地址了,就会报链接错误

而编译过程和链接过程合起来就是翻译过程。用C语言写的源文件,经过翻译过程就形成了可执行程序,就能够被计算机识别并且执行了。

使用gcc完成以上步骤

假设有2个源文件,分别是Test.c和Add.c。

预编译,分别形成Test.i和Add.i。

gcc -E Test.c -o Test.i
gcc -E Add.c -o Add.i

编译,分别形成Test.s和Add.s。

gcc -S Test.i -o Test.s
gcc -S Add.i -o Add.s

汇编,分别形成Test.o和Add.o。

gcc -c Test.s -o Test.o
gcc -c Add.s -o Add.o

链接,形成Proc可执行程序。

gcc Test.o Add.o -o Proc

运行Proc。

./Proc

记忆方式:ESc, iso。其中,ESc就是键盘左上角的esc键(注意大小写不要搞错了)。iso也就是镜像文件,装过系统的朋友应该知道。不知道的就记成ios就行了,这个总知道吧。

总结

  1. 每个C源程序都会单独经过编译器,进行编译过程,形成多个目标文件,接着由链接器进行链接过程,把多个目标文件和一些库链接起来,形成可执行程序,就可以被计算机识别并且执行了。其中编译过程和链接过程合称翻译过程,执行的过程被称为运行过程。翻译依赖的环境(编译器和链接器)被称为翻译环境,执行依赖的环境被称为运行环境。
  2. 编译过程分为3个小阶段,分别是:预编译,编译,汇编。
  3. 预编译主要进行文本操作,该过程结束后,文件内部仍然是C语言代码。
  4. 编译会把C语言代码翻译成汇编代码。这个过程中主要的行为是:语法分析、词法分析、语义分析和符号汇总。
  5. 汇编会把汇编代码翻译成二进制代码,形成目标文件。这个过程中,主要的行为是形成符号表。
  6. 链接过程中,会把多个目标文件,和一些库,链接形成可执行程序。这个过程中,主要的行为是:合并段表,以及符号表的合并和重定向。
  7. 命令行:ESc, iso。

感谢大家的阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGIS Pro二次开发】(12):txt文件和Excel文件的读写

在Arcgis Pro的工作流中&#xff0c;数据的输入是很常见的。这里以TXT和Excel两种文件为例&#xff0c;在SDK中实现数据的读取和写入。 一、txt文件的读写 txt文件的读写相对简单&#xff0c;可以用Arcgis Pro自带的OpenItemDialog打开txt文件&#xff0c;并直接读取&#xff…

Java稀疏数组的应用

文章目录需求存储结构分析问题稀疏数组稀疏数组存储结构整体思路代码示例需求 编写一个五子棋程序&#xff0c;可以完成存盘退出和继续上局的功能。这时就会涉及到棋盘当前棋子状态数据的保存和读取 黑色棋子为&#xff1a;1&#xff0c;白色棋子为&#xff1a;2&#xff0c;0…

BERT: Pre-training of Deep Bidirectional Transformers forLanguage Understanding

参考BERT原文[1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (arxiv.org)【(强推)李宏毅2021/2022春机器学习课程】 https://www.bilibili.com/video/BV1Wv411h7kN/?p73&share_sourcecopy_web&vd_source30e93e9c70e…

Less 运行环境

文章目录Less 运行环境概述运行Less方式一&#xff1a;浏览器环境方式二&#xff1a;koala编译器方式四&#xff1a;Node环境下编译Less 运行环境 概述 Less &#xff08;Leaner Style Sheets 的缩写&#xff09; 是一门向后兼容的 CSS 扩展语言。这里呈现的是 Less 的官方文…

ChatGPT能够干翻谷歌吗?

目前大多数人对于ChatGPT的喜爱&#xff0c;主要源自于其强大的沟通能力&#xff0c;当我们向ChatGPT提出问题时&#xff0c;它不仅能够为我们提供结论&#xff0c;而且还能够与我们建立沟通&#xff0c;向ChatGPT提出任何问题&#xff0c;感觉都像是在与一个真实的人类进行交谈…

蓝桥杯备考

数论&#xff1a;判断素数&#xff0c;鸽笼定理&#xff0c;抽屉理论 注意事项&#xff1a; 组合剪枝&#xff1a;i < n - (k - path.size()) 1 long类型的数后面要加L long s 2658417853L; 保留几位小数&#xff1a; System.out.printf(“%.2f”, arg); 四舍五入问题…

【Python】如何实现Redis构造简易客户端(教程在这)

文章目录前言一、准备二、原理剖析三、编写简易Redis客户端总结前言 Redis 是我们在开发过程中经常会用到的内存数据库&#xff0c;尤其是在Python的第三方模块Redis-py的支持下&#xff0c;在Python中使用Redis及其方便。 但是在有些情况下&#xff0c;我们无法使用像Redis-…

学习 Python 之 Pygame 开发魂斗罗(十四)

学习 Python 之 Pygame 开发魂斗罗&#xff08;十四&#xff09;继续编写魂斗罗1. 创建桥类2. 在主类中加入一些类变量3. 显示桥4. 解决玩家与桥的碰撞体问题5. 解决敌人与桥的碰撞体问题继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;十三&#x…

Visual Studio Code 1.77 发布,扩展的 GitHub Copilot 集成

VS Code 1.77 已发布&#xff0c;此版本一些主要亮点包括&#xff1a; Accessibility 改进 - 用于悬停、通知和 Sticky Scroll 的新键盘快捷键。 、down、home、end、page up和page down 键来聚焦悬停控件并进行水平和垂直滚动。聚焦悬停控件的键盘快捷键(CtrlK CtrlI)与用于在…

ArduPilot飞控之DIY-F450计划

ArduPilot飞控之DIY-F450计划1. 历史2. 源由3. 计划3.1 硬件3.2 软件4. 动手4.1 接线4.1.1 ELRS nano接收机4.1.2 BN880 GPS模块4.1.3 Radio Telemetry4.2 配置4.2.1 选择四轴机型4.2.2 电源参数调整4.2.3 校准加速度计4.2.4 校准磁力计4.2.5 遥控器校准4.2.6 电机设置4.2.7 电…

Linux- 系统随你玩之--玩出花活的命令浏览器上

文章目录1、背景2、命令浏览器2.1、命令浏览器介绍2.2、特点2.3 常用功能选项3、实操3.1、使用 wget 下载文件3.2、 断点续传3.3、镜像整个站点4、 总结1、背景 一位友人说他有台服务器&#xff0c;需要下载一个文件&#xff0c;但是没有视窗界面与下载工具&#xff0c;怎么办…

360周鸿祎离婚老婆能分得90亿,如果奶茶妹妹离婚会不会分走更多?

‍数据智能产业创新服务媒体——聚焦数智 改变商业最近&#xff0c;中国互联网界又有一个新鲜的大瓜——360周鸿祎与其老婆离婚&#xff0c;对方分走了近90亿。根据360发布的公告&#xff0c;董事长周鸿祎与胡欢经友好协商&#xff0c;已办理解除婚姻关系手续&#xff0c;并就…

不敲代码用ChatGPT开发一个App

先说下背景&#xff0c;有一天我在想 ChatGPT 对于成熟的开发者来说已经是一个非常靠谱的助手了&#xff0c;身边也确实有很多同事把它作为一个离不开的助理担当。 但是如果我只是略微懂一点前端知识的新人&#xff0c;了解 HTML、CSS、JS 相关的知识&#xff0c;想开发一个安…

智慧水务信息化平台建设,实现供水一体化管控

平台概述 柳林智慧水务系统平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生态、水…

技术分享| 什么是动态更新?

近期工作提到动态更新比较多&#xff0c;今天也借此机会&#xff0c;梳理一下相关的机制原理同大家分享。 动态机制及技术原理 动态研发模式就是一种基于云端的移动应用开发方法&#xff0c;主要能让开发者快速构建和发布多端的移动应用&#xff0c;实现业务的敏捷迭代和热更…

自动化篇 | 13 | app自动化:airtest

1 airtes简介 1.1 参考地址 http://airtest.netease.com/ # airtest官网 https://airtest.doc.io.netease.com/ # airtest操作方法 https://airtest.doc.io.netease.com/IDEdocs/faq/1_code_examples/ # 代码示例2 airtest架构 Airtest框架 3 airtest使用 3.1 打开界…

深度学习 - PyTorch入门

PyTorch入门前言张量Tensor导入torch创建张量返回numpy.ndarraytensor与list/ndarray/dataframe转化&#xff1a;tensor内数据类型转化维度变换0维item()&#xff1a;reshape()&#xff1a;squeeze&unsqueeze&#xff1a;permute&#xff1a;张量相关操作item()zeros() &am…

十二星座,各适合骑什么牌子的自行车

很多骑友喜欢研究星座&#xff0c;但并不大明白自己是什么星座&#xff0c;什么属性什么系&#xff0c;更不明白适合骑哪款自行车&#xff0c;下面大致说一下。处女座&#xff1a;适合骑共享单车&#xff0c;以黑色为主色调。摩羯座&#xff1a;适合骑提安特自行车&#xff0c;…

[Python] 循环语句

循环语句就是在符合条件的情况下&#xff0c;重复执行一个代码段 1.while循环 while语句可用于在条件为真时反复执行代码块 语法格式 while 条件语句:执行语句 当条件语句为真(True)时&#xff0c;就会执行while循环下的语句 示例 实现1到100 的累加并输出求和结果 …

线性代数 --- 最小二乘在直线拟合上的应用与Gram-Schmidt正交化

最小二乘在直线拟合上的应用 在前一篇最小二乘的文章中&#xff1a; 线性代数 --- 投影与最小二乘 下(多元方程组的最小二乘解与向量在多维子空间上的投影)_松下J27的博客-CSDN博客多变量方程组的最小二乘&#xff0c;向量到多维子空间上的投影。https://blog.csdn.net/daduzi…
最新文章