前言:
写了这么长时间的c语言代码,我们都没有想过为什么要写头文件吗,而程序执行的过程又是如何呢?只有编译吗 ?要知道机器只能读懂二进制但我们所写的代码编译器是如何读懂的呢?这一篇文章看后一定能解决以上问题同时能够让你深入的理解程序执行的过程。^ _ ^
💞 💞 欢迎来到小马学习代码博客!!!!
目录
一、程序执行的过程:
1.1前提准备:
为了更好的演示过程这里我没有用Xcode同时也用不了VS2019,因为这样的编译器无法观察编译的具体过程,这里我用的是gcc编译器能够更好的演示在程序运行过程中的每一个过程!!!
1.2 程序执行过程图:
1.2.1 在一个项目中会有不同的.c 文件的形成就好比我上一次写的一个通讯录程序就是有两个.c文件和一个.h文件来完成的一个通讯录而.c文件就是程序的源文件,他在编译的操作下变成了目标文件,而又通过链接器把我们的目标文件和链接库合并成一个可执行的程序。而编译器是如何将源文件变成目标文件呢?如下图 ☟☟☟☟☟
二、编译器的几个阶段:
2.1编译器阶段的过程图:
2.1.1在程序执行过程中我们从上面图可以知道编译本身也是分为几个阶段来进行的:预编译阶段,编译阶段,汇编阶段。而每个阶段都有自己相应的功能接下来我来通过gcc来演示不同阶段的功能实现!!!
2.2通过一个代码分析:
#include "text.hpp"
#define M 100
#include <stdio.h>
int g_val=2022;
int add(int x,int y)
{
return x+y;
}
//这是一个注释为了方便观察预处理
int main()
{
int m =M;
int arr[]={1,2,3,4,5,6,7,8,9,10};
int i=0;
for(i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
printf("%d ",g_val);
return 0;
}
2.3预处理阶段:
2.3.1这是在gcc 编译器下我来演示的 gcc test.c -E -o test.i 这个命令是使test.c文件只执行到预处理阶段而就结束了,我们发现就出现一个test.i文件
我们通过vim编译器进去观察一下
对比没有预处理前的代码:
对比发现在预处理阶段前面多了800多行代码(这里不好展示小马就省略展示了^ _ ^)他就是头文件的展开,同时发想define定义符号的替换删除定义符号(m定义为M发现在预处理阶段变成100了),同时我们的注释在预处理阶段也没有了。
这里我么可以总结一下:
在预处理阶段完成了:《头文件的展开》《define定义符号的替换删除定义的符号》《注释删除》。
2.4编译阶段:
2.4.1gcc test.i -S这个指令是将test.i文件完成编译阶段就停止,同时生成一个test.s文件
进行保存翻译过程的信息。
我们在此通过vim编译器进行观察👀
我们发现我们写的代码已经被翻译成了汇编语言(这里我也只是为了方便只是展示了一部分^ _ ^)这里编译器经过了:语法分析、词法分析、语义分析、和符号汇总四个过程将我们的代码编程汇编语言,这个过程并不是简单的。而符号汇总是把全局变量的符号进行汇总起来,为了下面的过程提供帮助。
2.5汇编过程:
2.5.1 gcc test.s -c这个命令将test.s 文件进行汇编同时生成一个test.o文件,就是把test.s汇编文件转化为机器能够读懂的二进制文件!!
我们通过vim编译器进行观察test.o文件 :
这里我们已经变成我们看不懂的二进制语言了(小马也看不懂二进制哈哈哈👀)这里编译器将我们的汇编语言变成机器能读懂的二进制语言,同时生成符号表(就是给我们的函数和全局变量生成地址方便链接的时候进行查找)
编译器通过这三个过程把我们的test.c文件变成了机器能读懂的test.o文件,最后通过链接器变成一个可执行的程序。
2.6程序的链接:
2.6.1gcc test.o命令就是将test.o文件中的符号表进行链接起来,最后生成一个可执行的a.out文件!!!
我们编译一下a.out文件
链接的作用:合成段表同时将符号表进行合成和重定位 最后形成了我们形成一个可以执行的程序。
总结:
通过我们的一步步演示,在看我们前言所提问的问题就能过很容的进行解决啦,一个程序的执行,并不是一个简单的编程这么简单,他是通过预处理,编译,汇编最后链接这几个步骤才形成一个可执行的程序,而四个步骤每一个步骤都是至关重要的也是缺一不少了,我们通过简单的了解他的执行过程能过让我们深入的理解程序的执行过程,对以后的问题就能够有更深的理解啦^ _ ^
最后小马码文不易,如果觉得有帮助就多多支持哈!!!^ _ ^