预处理
一、什么是预处理程序
预处理语句是一些行首以#开始的特殊语句,例如:#include,#define等。预处理器在其它编译处理(词法分析、语法分析、代码生成、优化和连接等)之前,先进行这些语句的分析处理。
// 预处理 即 编译之前的处理。
预处理语句使用的目的在于帮助程序员编写出易读、易改、易移植并便于调试的程序。
预处理语句主要有四种:
宏定义和宏替换、文件包含、条件编译和行控制。
预处理语句的作用范围是从被定义语句开始直至被解除定义或是到包含它的文件结术为止均有效。
二、宏定义和宏替换?
“宏”是借用汇编语言中的概念。为的是在C语言程序中方便的作一些定义和扩展。这些语句以#define开头,分为两种:符号常量的宏定义和带参数的宏定义。
// 预处理(预编译)工作也叫做宏展开:将宏名替换为相应文本块
1.符号常量的宏定义和宏替换?
#define 标识符 字符串?
其中标识符就叫作宏名称。
2.带有参数的宏定义及其替换?
#define 标识符(参数列表) 字符串?
对带有参数的宏定义进行宏替换时,不仅对宏标识符作字符串替换,还必须作参数的替换。
例如:?
#define SQ_ERR(x) (x*x)?
SQ_ERR(a+B)将被宏替换成(a+B*a+B)。?
宏定义也可嵌套使用
例如:?
#define SQ(x) ((x)*(x))?
#define CUBE(x) (SQ(x)*(x))?
3.宏定义类函数?
宏定义常用于把直接插入的代码来代替函数,省去调用和返回过程,以提高执行效率。
这一类的宏,就称做宏定义类函数。
例如:?
#define MIN(x,y) (((x)<(y))?(x):(y))?
这样
m=MIN(a,B);
将被预处理成:
m=(((a)<(B))?(a):(B));?
不带参宏说明:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。
例如:数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义可以嵌套
(8)字符串""中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
带参宏说明:
类似于函数调用,有一个哑实结合的过程
(1)实参如果是表达式容易出问题
#define S(r) r*r
area=S(a+B);第一步换为area=r*r;,第二步被换为area=a+b*a+b;
正确的宏定义是#define S(r) (r)*(r)
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解 // ?
(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)函数只有一个返回值,利用宏则可以设法得到多个值
(7)宏展开使源程序变长,函数调用不会
(8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)
三、文件包含?
文件包含是指一个程序文件将另一个指定义文件的内容包含进来,用#include语句来说明。
一般有两种格式:?
(1) #include <文件名>?
(2) #include 〃文件名〃?
第一种,用尖括号表示在标准库目录下找该文件;
第二种,用双引号表示先在当前目录(源文件所在目录)中找包含文件,若找不到,再到标准库目录中找。
系统的标准库文件都是.h文件。
例如:?
#include <stdio.h> /* 标准输入输出的基本常量和宏或函数文件 */?
#include <string.h> /* 串函数文件 */?
#include <malloc.h> /* 内存分配函数文件 */?
#include <ctype.h> /* 字符函数文件 */?
#include <math.h> /* 数学函数库文件 */?
用文件包含,可以减少重复工作,提高程序正确性,还便于维护修改。程序员可以把自己常用的一些符号常量、类型定义和带参数的宏定义,以及一些常用自编函数都放在.h文件中,通过#include语句包含引用之。?
编译时以包含处理以后的文件为编译单位,被包含的文件是源文件的一部分。
编译以后只得到一个目标文件.obj
被包含的文件又被称为“头部文件”、“头文件”,并且常用.h作扩展名。
修改头文件后所有包含该文件的文件都要重新编译
头文件的内容除了函数原型和宏定义外,还可以有结构体定义,全局变量定义 // ?
(1)一个#include命令指定一个头文件
(2)文件1包含文件2,文件2用到文件3,则文件3的包含命令#include应放在文件1的头部第一行。// ?
(3)包含可以嵌套 // ?
(4)<文件名>称为标准方式,系统到头文件目录查找文件;
"文件名"则先在当前目录查找,而后到头文件目录查找
(5)被包含文件中的静态全局变量不用在包含文件中声明。// 本来就是要定义为模块级
四、条件编译?
提供条件编译措施使同一源程序可以根据不同编译条件(参数)产生不同的目标代码,其作用在于便于调试和移植。?
条件编译控制语句有不同形式
#if/ifdef/ifndef #elif #else #endif
举例如下:
#define A 3
#define B 4
#if A==B
// 这里的代码,预处理时被忽略,即不参与编译
#else if A>B
// 这里也是
#else
// 如果A char ch1,ch2;
int iv1, iv2;
#endif
main()
{
ch1 = 'x';
puts("你定义A、B的值满足 A}
本章小结
1. 预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程序员在程序中用预处理命令来调用这些功能。
2. 宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在宏调用中将用该字符串代换宏名。
3. 宏扩展的本质很简单- 文本替换。
4. 为了避免宏代换时发生错误,宏定义中宏参数加上括号,同时也要避免像 max(i++, j)这种有副作用的用法。
5. 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
6. 条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
7. 使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。
阅读(1669) | 评论(0) | 转发(0) |