C语言重点篇:详解程序环境和预处理
程序环境
大家是否想过从我们编写的代码到最后在屏幕上打印的结果,这中间都经历了些什么了?
其实,在ANSI C(标准C)的任何一种实现中,都存在两个不同的环境,即翻译环境和执行环境。翻译环境,在这个环境中,代码被转换为可执行的机器指令。执行环境,它用于实际执行代码。我们运行代码时都将经历这两个过程,最后才将运行结果呈现在屏幕上。
针对整个程序环境,首先先给出一个大体的图,这样读者可以先理解大概。
翻译环境
翻译环境由编译和链接组成
接下来我们了解下编译的各个组成部分
编译
编译=预编译+编译+汇编,下面来看看每一步都做了些什么?
预编译
在预编译这个环节,编译器主要会对C代码做这三件事:
1.头文件的包含。 在预编译阶段,编译器会将代码中所包含的头文件都替换成头文件的内容。例如,#include <stdio.h>这句代码会被替换成stdio.h这个头文件中的全部代码。
2.注释的删除。 在预编译阶段,编译器会将代码中所有的注释都删去,这样可以减少代码量,毕竟运行代码的时候编译器也不用看你的注释。
3.#define定义符号的替换。 我们都知道#define定义的标识符也好,定义的宏也罢,都是起到替换的作用,而真正进行替换的时刻便是预编译阶段。
这些都是一些文本操作
编译
编译阶段的主要任务就是将C代码翻译成汇编指令,这个过程主要包括这四个步骤:
1.词法分析。 词法分析的任务是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。
2.语法分析。 编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最.后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。
3.语义分析。 语义分析是编译过程的一个逻辑阶段, 语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。
4.符文汇总。 在这个环节中,会将每个源文件的全局范围的变量符号进行汇总.
汇编
注:因为test.c文件中提取的符号Add只是Add函数的声明,并不是定义,无法判断Add函数是否真正存在,所以test.c生成符号表时分配给Add符号的地址是一个无意义(非法)的地址。
链接
1.合并段表。 其实,汇编结束后所生成的obj文件内部会被划分为几个段,在链接过程中就会把每个obj文件对应的段通过某种规则合并起来,最后形成可执行程序(.exe为后缀)。
2.符号表的合并和重定位。 在链接期间会将每个源文件的符号表进行合并,若不同源文件的符号表中出现了相同的符号,则取合法的地址为合并后的地址(重定位)。
注:符号表并非无意义。例如,当要调用某一函数时,编译器会在符号表中查找该符号,若有,则调用成功,否则调用失败。
执行环境(运行环境)
1.程序首先载入内存中。 在有操作系统的环境中,该操作一般由操作系统来完成。在独立的环境中,程序的载入可以由手工完成,也可以通过可执行代码置入只读内存来完成。
2.程序的执行开始。 接着便调用main函数。
3.开始执行程序代码。 这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留它们的值。
4.终止程序。 正常终止main函数,也可能是以外终止。
针对第3步,程序运行时使用一个运行时堆栈,下面画了一幅图,这个知识涉及到函数栈帧的创建和销毁。