目录
大纲
翻译环境和运行环境
在ANSI C的任何一种实现中,存在两个不同的环境,一个是翻译环境,另一个是执行环境。
翻译环境中包含了预处理(或者称为预编译),编译,汇编三个步骤。
预处理
在预处理阶段,源文件和头文件会被处理成为.i为后缀的文件。
处理的规则如下:
- 将所有的#define删除,并展开所有宏定义。
- 处理所有的条件指令,如:#if、#ifedf、#elif、#else、#endif。
- 处理#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
- 删除所有的注释。
- 添加行号和文件名标识,方便后续编译器生成调试信息。
- 或保留所有的#pragma的编译器指令,编译器后续会使用。
编译
编译过程分为三步:词法分析、语义分析、语义分析及优化。
词法分析
将源代码程序被输入扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。
语法分析
语法分析将对扫描的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点的树。
语义分析
由语义分析器来完成语义分析,即对表达式的语法层面分析。编译器能做的分析是语法的静态分析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。
汇编
汇编器是将汇编代码转转变成机器可执行的指令,每一个汇编语句几乎都对应一条机器指令。
就是根据会变质量和机器指令的对照表一一进行翻译,也不做指令优化。
链接
链接是一个复杂的过程,主要包括:地址和空间分配、符号决议和重定位等这些步骤。
链接解决的是一个项目多文件、多模块只见相互调用的问题。
预处理详细介绍
预定义符号
C语言设置了一些预定义的符号,可以直接使用,预定义符号也是在预处理期间的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#define定义常量
#define MAX 1000
注意:define定义标识符的时候,不需要在结尾处加上分号(;)。
#define定义宏
#define极致包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏,或者定义宏。
宏的声明方式
#define name(parament-list) stuff
其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中。
举例:
#define DOUBLE(x) ((x)*(X))
#include <stdio.h>
int main()
{
int a = 5;
printf("%d",DOUBLE(a));
return 0;
}
在定义宏的时候为了避免由于参数中操作符和临近操作符之间不可预料的相互作用,所以不要吝啬括号。
带有副作用的宏参数
x+1;//不带副作用
x++;//带有副作用
举例:
#deine MAX(a,b) ((a) > (b) ? (a) : (b))
#include <stdio.h>
int main()
{
int x = 5;
int y = 8;
int z = MAX(a++,y++);
printf("%d %d %d",x, y, z);
}
输出结果:x=8 y=10 z=9。
宏替换的规则
- 在调用宏的时候,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换。
- 替换文本随后被插入到程序中原本文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意,对于宏,不能出现递归。以及字符串常量的内容并不被搜索。
宏和函数的对比
- 宏比函数在程序的规模和速度方面更胜一筹。
- 宏与类型无关。
- 宏没法调试。
- 宏与类型无关。
- 宏可能会带来运算符优先级的问题,导致程序容易出现错。
- 宏的参数可以出现类型。
命令行定义
C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
条件编译
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多个分⽀的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
头文件包含
#include "filename"
库文件包含
#include <stdio.h>