前言
早期的程序员,使用的是二进制语言编写代码,之后发展出汇编语言,B语言,C语言等等,其中,二进制语言是机器可以识别的语言,但对于程序员来说理解可能较为困难,发展到C语言时,我们所写的代码已经经过不断优化,使得程序员能清晰的明白代码的含义,但最终,我们要想机器识别出我们所写的代码,还需要把所写代码转换为机器指令
一、程序环境
什么是程序环境?
在ANSI C的任何一种实现中,都存在两个不同的环境
第一个是翻译环境,用于将我们所写的源代码转换为机器所能识别的机器指令,也就是二进制指令
第二个是执行环境,用于实际执行代码
其中翻译环境又包括预编译(预处理),编译,汇编,链接几个步骤,
源代码从编译到执行一共经历哪些步骤呢?
如图,源文件会经过预编译、编译、汇编、链接后,生成可执行程序(后缀为.exe),最后执行
二、编译环境
为了更好的观察编译的不同阶段所发生的情况,我们在VSCode上搭建了gcc环境,在gcc环境下来逐步剖析编译阶段对源代码做的处理
2.1预编译(预处理)阶段
如图,我们创建了一个test.c源文件,内容有头文件的包含,#define定义的标识符MAX,一行注释,创建并输出一个拥有MAX个元素的整型数组
当我们在文本行输入指令gcc test.c -E -o test.i
代表的意思是在gcc环境下,对test.c文件进行预处理(-E),并将结果输出(-o)到test.i中,当我们敲下回车后,左边生成了test.i文件,
让我们来看看源文件经过预处理后会发现哪些变化
1.在main函数前,多了800多行未知代码
2.在源文件所写的注释不见了
3.#defien定义的标识符MAX全部被10所替代
结合我们所写的全部内容,不难推断出开头的800多行代码就是包含的头文件的内容,同时我们也可以通过与stdio.h头文件的对比判断出,确实是包含的头文件的内容(并且做了简化处理)
2.2编译阶段
在文本行(终端)输入指令gcc test.i -S
表示在gcc环境下对test.i进行编译,按下回车后,左边生成一个.s文件,其内容是将C语言翻译成的汇编代码,我们可以在vs2019环境下查看相同代码的汇编代码
除此之外,在编译阶段还有一些复杂的处理,如语法、词法、语义、分析,符号汇总 ,有兴趣者可以深入了解
2.3汇编阶段
作用:
1.将汇编代码转换为二进制指令,生成目标文件(也是二进制文件),在gcc环境下后缀为.o,在vs中后缀为.obj
2.形成符号表,有兴趣者可深入了解
输入指令:gcc test.s -c
2.4链接阶段
我们了解到到源文件在经过编译三步走之后,会生成一个后缀为.o(gcc环境下)的目标文件,事实上,每一个源文件都会由编译器单独的处理,生成与源文件相对于的目标文件,这些目标文件汇聚在一起,同链接库一起经链接器处理后,生成可执行程序(后缀为.exe),如图
输入指令gcc test.o -o test.exe
将目标文件和链接库经链接器处理,生成可执行程序,左边出现text.exe文件
三、执行环境
程序执行的过程:
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(
stack),存储函数的局部变量和返回
地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程
一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。