一、程序的翻译环境
翻译环境:组成程序的每个源文件通过编译器编译成目标文件,所有的目标文件在由连接器捆绑在一起形成一个单一而完整的可执行程序。同时,链接器也会引入标准c函数库中任何被该程序所有到的函数,而且他可以搜索程序员个人的程序库将其需要的函数也链接到程序中。
程序编译连接过程:程序的编译和和链接包括程序的预处理、编译、汇编和链接4个步骤,每个步骤都由其具体的任务。
1.预处理
预处理主要有展开头文件、宏替换、条件编译、去掉注释四个任务,经过这4个步骤最终生成一个' .i '文件(Linux环境下,不同环境会有所不同)。
展开头文件:在程序中,使用到一些库函数或者其他文件中的函数时都要包含其所在文件。程序会在预处理阶段将这些头文件中的函数展开在该文件前,当函数执行到这些函数时会直接在展开的这些文件中找有没有所需要的的函数,如果没有可能就会报错。
宏替换:宏是一种替换机制,因此在预处理阶段,会将定义的宏按照定义进行替换。
条件编译:条件编译是指预处理器根据条件编译指令,有条件地选择源程序代码中的一部分代码作为输出,送给编译器进行编译。主要是为了有选择性地执行相应操作,防止宏替换内容(如文件等)的重复包含。
去掉注释:预编译过程中,预编译器会对程序中的注释进行删除。
2.编译
编译过程主要任务就是检查语法、生成汇编代码(生成“ . s”文件)。也就是说,我们写的程序如果有语法错误,在编译阶段就会被检查出来,如果不是语法错误编译阶段是检查 不出来的。
3.汇编:将汇编代码转成二进制机器码(.o文件),这里的汇编和编译过程中的汇编是不一样的,这里的汇编是指生成二进制机器码,而上边的汇编是指生成汇编指令。

4.链接:将目标文件链接到一起,生成可执行文件(.exe文件)。
二、程序的运行环境
程序执行过程:
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同 时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
三、预处理详解
1.预定义符号(语言内置好的)
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
使用举例:
int main()
{
printf("当前文件的路径:%s\n当前代码的行号:%d\n", __FILE__, __LINE__);
printf("文件编译日期:%s\n",__DATE__);
printf("文件编译时间:%s\n", __TIME__);
return 0;
}
运行结果:
2.#define
#define定义标志符
语法:#define name stuff
例子:
#define MAX 10000 //定义一个宏常量,用10000替换
#define REG register //为关键字定义一个简短的名字
//如果定义的stuff过长,可以分成几行写,除了最后一行其他行的结尾都加上\(续行符)
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
注意:宏定义常量的时候,不能加;号。例如: #define MAX 10000;后边的;不能加,因为宏为替换机制,如果加上分号就会出现错误。例如:
#define MAX 1000;
int mian()
{
int num[MAX];//错误,int a = MAX //正确,注意这条语句没有写;,而是直接利用宏替换的;
return 0;
}
#define宏定义
#defifine 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(defifine macro)。
分析下面程序的运行结果:
PROGRAM ONE
#define add(a,b) a+b
printf("%d\n",add(2,3));
//毫无疑问,这个运行结果就是2+3=5
printf("%d\n",add(2,3)*5);
//这里,2+3*5=17
PROGRAM TWO
#define add(a,b) (a+b)
printf("%d\n",add(2,3));
//(2+3)=5
printf("%d\n", add(2 , 3)*5);
//(2+3)*5=25
PROGRAM TREE
#define mul(a,b) a*b
printf("%d\n",add(2+3,5));
//2+3*5=17
#define mul(a,b) (a)*(b)
printf("%d\n",add(2+3,5));
//(2+3)*(5)=25
#define替换规则
在程序中扩展#defifine定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#defifine定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#defifine定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#defifine 定义中可以出现其他#defifine定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#defifine定义的符号的时候,字符串常量的内容并不被搜索。