从Linux角度具体理解程序翻译过程-----预处理、编译、汇编、链接

前言:

在C语言中,我们知道程序从我们所写的代码到可执行执行的过程中经历了以下过程

1.预处理

2.编译

3.汇编

4.链接

 可以通过下图来理解

翻译过程

1.预处理

该过程主要进行以下操作:

(1)头文件的包含

(2)define定义符号的替换,删除定义的符号(也就是宏定义) 

(3)注释的删除

(4)条件编译的处理(#ifdef, #else, #endif)

2.编译

(1)把C语言代码转化为汇编代码

3.汇编

(1)把汇编代码替换为机器指令(也可以说是机器指令,目标代码),形成符号表

(2)语法分析,此法分析,符号汇总,语义分析

4.链接

(1)合并段表

(2)符号表的合并和重定位

想知道程序的翻译环境和执行环境的详细过程,请移步:(暂未完成,敬请期待)

Linux下对其理解:

首先,我的codetest.c中的代码如下:

#include <stdio.h>//宏定义
#define AC return
#define please 0int main()
{printf("hello Linux\n");//注释部分// printf("hello Linux1\n");// printf("hello Linux2\n");// printf("hello Linux3\n");int* p = NULL;printf("%d\n", sizeof(p));//条件编译语句
#ifdef MAXprintf("MAX exist\n");
#elseprintf("MAX cannot find\n");
#endif//下面只是为了节目效果,请大家不要效仿,这是不好的编程习惯,我之前也从未这样子写过AC please;
}

1.预处理

从那篇文章我们知道了预处理之后,我们的test.c会变成test.i文件

{

拓展:

要注意,这是在windows环境下,在Linux环境下并不以后缀区分文件类型,而是通过:ll 指令,会显示如下信息:

文件类型文件权限硬链接数文件拥有者文件所属组文件大小(以字节为单位)文件创建时间或者最近更新使劲按文件名
-rw-rw-r-- 1

xkjtx

xkjtx  627Apr 18 21:32codetest
drwxr-xr-x 2xkjtxxkjtx    6Apr 13 19:42Desktop

Linux下文件信息:

在Linux中,输入ll命令会显示文件和目录的详细信息。这些信息通常分为若干列,每列的含义如下:

文件类型:

第1个字符代表文件类型:
-:普通文件
d:目录
l:软链接(符号链接)
c:字符设备文件
b:块设备文件
s:本地套接字
p:命名管道(FIFO)


接下来的九个字符分为三组,每组三个字符,代表文件权限:
r:可读权限
w:可写权限
x:可执行权限

每组分别代表文件拥有者(user)、所属组(group)和其他用户(other)的权限

硬链接数:

对于文件,这表示有多少个文件名指向该文件;对于目录,这表示该目录所含的子目录数(不包括.和..)。

文件拥有者:

显示文件的拥有者用户名。

文件所属组:

显示文件所属的用户组名称。

这个组存在的意义是:有时为了保护文件所属者的信息,文件只有自己可见,但这样子会有弊端:当文件所有者的同事或者上级想查看文件时,无法查看,只能打开other查看的权限,但是这样子所有人都可以看,而不只是上级和同事,因此设置了group(文件所属组)权限,设置了其他人可见,并保证了隐私

other:

不属于   文件拥有者   和   文件所属组   中的任何一者

}

因此当我们在Linux输入以下命令行:

其中:

-E:表示预处理

codetest.c:我们的代码所属文件

-o:理解为起别名

codetest.i:预处理后的文件名

gcc -E codetest.c -o codetest.i
//     代码文件名   预处理后的文件名

在输入:ls后会发现列表中多了一个文件:codetest.i

我们再使用以下命令:

vim codetest.c

进入codetest.c文件,再输入

:vs codetest.i

使其分屏操作,我们平时也可以用这种方式对比两个源文件的不同之处(写题的人都懂这种操作有多么重要吧)

以下左半部分为codetest.i,右半部分为codetest.c

图一:注意左侧高亮部分,这是我们在codetest.c文件中对stdio.h的包含在预处理好的文件中的展示,其实codetest.i中前800多行都是包含了stdio.h文件里的内容,这也就是我们所说的预处理第一个步骤:头文件的包含

 图二:

可以发现注释部分在codetest.i中消失了

条件编译中,因为MAX我们没有在宏定义中定义,因此它不执行:printf("MAX exist\n");

而是执行: printf("MAX cannot find\n");

而codetest.c中的AC please被替换为return 0;

这里也就解释了 :

1.预处理

该过程主要进行以下操作:

(1)头文件的包含{codetest.i中前800多行都是包含了stdio.h文件里的内容}

(2)define定义符号的替换,删除定义的符号(也就是宏定义) 

(3)注释的删除{注释部分在codetest.i中消失了;}

(4)条件编译的处理(#ifdef, #else, #endif)

{执行: printf("MAX cannot find\n");}

2.编译

从那篇文章我们知道了编译之后,我们的test.i会变成test.s文件

接下来,我们在Linux的命令行中输入:

gcc -S codetest.i -o codetest.s
//代码文件名(写成codetest.c也行,只是要再次预处理)   编译后的文件名

其中:

-S:表示编译

codetest.i:我们的代码所属文件(写成codetest.c也行,只是要再次预处理)

-o:理解为起别名

codetest.s:生成的编译后的文件名

再次输入:ls,就会发现多了个codetest.s文件 

使用以下命令:

vim codetest.s

就会出现以下样子

包括了movl这样的注记符,还有操作数,寄存器相关信息

这就是汇编代码,这也解释了:

2.编译

(1)把C语言代码转化为汇编代码

3.汇编

从那篇文章我们知道了汇编之后,我们的codetest.s会变成codetest.o文件

接下来,我们在Linux的命令行中输入:

gcc -c codetest.s -o codetest.o
//代码文件名(写成codetest.c,codetest.i,codetest.s也行,只是要再次进行之前的操作)   编译后的文件名

其中:

-c:表示编译(注意是小写)

codetest.s:我们的代码所属文件

-o:理解为起别名

codetest.o:编译后的文件名

再次输入:ls,就会发现多了个codetest.o文件 

再次进行以下操作:

使用以下命令:

vim codetest.o

就会出现以下样子 

这就是二进制,这也解释了:

3.汇编

(1)把汇编代码替换为机器指令(就是2进制指令)

4.链接

这一步不用像之前一样写-E,-S,-c这样子的命令,因为gcc自带了链接功能,直接输入以下代码就好:

gcc codetest.o -o codetest
// 代码文件名      生成的可执行文件名

输入ls,会发现多了个绿色的codetest文件

再次输入

./codetest

 执行codetest.c代码,输出以下部分

以上就是通过Linux查看C语言翻译环境的所有过程啦~~(预处理,编译,汇编,链接)

以下是识记小部分,

 * 你是不是被什么-E,-S,-c指令搞蒙了?去掉-看看,是不是就算ESc啊?键盘左上角的键哦  *

 * 是不是被.i  .s   .o 搞懵了?如果你装的是虚拟机,你就会发现iso就是你装在虚拟机上的镜像文件的后缀名哦,注意,这不是ios操作系统的ios,是iso,不要搞错了 ~~~  *

完结撒花~~哦吼吼~~

喜欢的给俺点点赞哦~

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

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

相关文章

C语言——九九乘法表

直接上代码实现九九乘法表&#xff0c;完整代码如下&#xff1a; #include <stdio.h>int main(){int i,j;for(i1;i<10;i){for(j1;j<i;j){printf("%d*%d%d\t",j,i,i*j);/*printf("%d*%d%-2d",j,i,i*j);*/}printf("\n");}return 0; }…

常用的7个免费ChatGPT网站

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

SpringCloud系列(5)--SpringCloud微服务工程公共部分提取

前言&#xff1a;在上一章节中我们创建了两个个SpringCloud工程&#xff0c;但在两个工程中分别存在着一些重复的部分&#xff0c;例如重复的实体类&#xff08;如图所示&#xff09;&#xff0c;这样会造成系统的冗余&#xff0c;所以我们需要把公共的类提取到一个工程里&…

41、二叉树-二叉树的层序遍历

思路&#xff1a; 层序遍历就是从左到右依次遍历。这个时候就可以使用队列的方式。例如先把头节点入队&#xff0c;然后遍历开始&#xff0c;首先计算队列长度&#xff0c;第一层&#xff0c;长度为了&#xff0c;遍历一次&#xff0c;依次出队&#xff0c;头结点出队&#xff…

Linux的学习之路:13、进程替代

摘要 本章主要是说一下进程替代用到的函数exec以及自己实现的简易shell 目录 摘要 一、进程程序替换 1、替换原理 2 、替换函数 3、函数解释与命令理解 4、代码演示 1、execl 2、execv 3、execlp 4、execvp 二、简易的myshell 三、代码 myshell exec 一、进程…

Ubuntu 22最新dockers部署redis哨兵模式,并整合spring boot的详细记录(含spring boot项目包)

dockers部署redis哨兵模式&#xff0c;并整合spring boot 环境说明相关学习博客一、在docker中安装redis1、下载dockers镜像包和redis配置文件&#xff08;主从一样&#xff09;2、编辑配置文件&#xff08;主从一样&#xff09;3、启动redis&#xff08;主从一样&#xff09;4…

【OpenGL实践08】现代渲染管线在GLUT和Pygame和Qt.QOpenGLWidget上各自的实现代码

Qt.QOpenGLWidget进行现代渲染管线实验效果 一、说明 据说QOpenGLWidget是用来取代QGLWidget的继承者&#xff0c;我们试图将GLUT上的旧代码改成QOpenGLWidget&#xff0c;本以为差别不大&#xff0c;轻易搞定&#xff0c;经实践发现要付出极大努力才能完成。经多次实验发现G…

工作流JBPM操作API组任务

文章目录 8.10 组任务8.10.1 组任务的分配8.10.1.1 写死的方式指定8.10.1.2 实现AssignmentHandler接口指定 8.10.2 查询自己的组任务8.10.3 办理自己的组任务 8.10 组任务8.10.1 组任务的分配8.10.1.1 写死的方式指定8.10.1.2 实现AssignmentHandler接口指定 8.10.2 查询自己的…

算法打卡day38

今日任务&#xff1a; 1&#xff09;完全背包理论基础(卡码网52. 携带研究材料) 2&#xff09;518.零钱兑换II 3&#xff09;377. 组合总和 Ⅳ 4&#xff09;复习day13 完全背包理论基础(卡码网52. 携带研究材料) 题目链接&#xff1a;52. 携带研究材料&#xff08;第七期模拟…

.NET 爬虫从入门到入狱

目录 前言 1.&#x1f4a1;使用HttpClient爬取数据 2.&#x1f680;模拟User-Agent 3.&#x1f935;使用HTML解析库 3.&#x1f44c;前端Price显示 4.&#x1f331;运行实例 获取金价Au 5.&#x1f9fe;使用正则表达式解析 6.&#x1f4ab;获取BTC价格 7.✨获取CSDN热点…

DevOps是什么?

DevOps是一系列实践、工具和文化理念的组合&#xff0c;旨在自动化并整合软件开发和信息技术运维团队之间的流程。以下是DevOps的几个关键点&#xff1a; 沟通与协作&#xff1a;DevOps强调开发和运维团队之间的沟通与合作&#xff0c;通过改善这两个部门间的协作关系&#xff…

关于MCU核心板的一些常见问题

BGA植球与焊接&#xff08;多涂焊油&#xff09;&#xff1a; 【BGA芯片是真麻烦&#xff0c;主要是植锡珠太麻烦了&#xff0c;拆一次就得重新植】https://www.bilibili.com/video/BV1vW4y1w7oNvd_source3cc3c07b09206097d0d8b0aefdf07958 / NC电容一般有两种含义&#xff1…