1、第七讲 编译预处理 教学要求 1.了解预编译的概念,掌握宏定义的方法。 2.了解“文件包含”与预处理的应用。 3.了解条件编译的几种形式。 7.1 概述 一、编译预处理概念 编译预处理是指,在进行编译之前,先对源程序中的编译预处理命令进行处理; 然后再将处理的结果, 和源程序一起进行编译, 以得到目标代码。 二、 种类 1、 宏定义: #define 2、 文件包含:#include 3、 条件编译:#if _ # else_ # endif 等 三、 格式 l “ # ”开头 l 占单独书写行 l 语句尾不加分号 7.2 宏定义 一、不带参数宏定义 1、
2、 一般形式: #define 宏名 [字符串] 2、 功能:用指定标识符(宏名)代替字符序列(宏体) 3、 定义位置:任意(一般在函数外面) 4、 作用域:从定义命令到文件结束 5、 # undef 可终止宏名作用域 格式: #undef 宏名 6、 宏展开:预编译时,用宏体替换宏名 -- 不作语法检查 7、 引号中的内容与宏名相同也不置换 8、 宏定义可嵌套,不能递归 9、 宏定义中使用必要的括号( ) 【例 7.1】给出下面宏替换的结果 #define R 5.0 #define FORMAT "Area =% f\n"
3、define PI 3.14159
#define AREA R*R*PI
#define PR printf
#include
4、78.539750 FORMAT 二、带参数宏定义 1、 一般形式: # define 宏名(参数表) 宏体 2、 宏展开:形参用实参换,其它字符保留 例: # define S( a, b ) a * b area = S( 3, 2 ); 宏展开: area = 3 * 2; 3、 宏名与左圆括号之间不能留有空格 例: # define S(r) PI*r*r 相当于定义了不带参宏 S, 代表字符串 “(r) PI*r*r” 4、 宏体及
5、各形参外一般应加括号 ( )
例: # define POWER(x) x*x
x = 4; y = 6;
z = POWER( x + y );
宏展开:z = x + y*x+y;
一般写成: # define POWER(x) ((x)*(x))
宏展开:z=((x+y)*(x+y));
【例7.2】用带参数宏定义求两个数的最大值
# define MAX(x, y) (x)>(y)?(x):(y)
# include
6、 a, b, c; float x, y, z; scanf("%d%d", &a,&b); c = MAX(a,b); //宏展开: c = (a)>(b)?(a):(b) scanf("%f%f", &x,&y); z = MAX(x, y); //宏展开: z = (x)>(y)?(x):(y) printf("c = %d z=%f \n", c, z); } 5、 带参的宏与函数区别 带参宏 函数 处理时间 编译时 程序运行时 参数类型 无类型问题 定义实参
7、形参类型 处理过程 不分配内存简单的字符置换 分配内存先求实参值,再代入形参 程序长度 变长 不变 运行速度 不占运行时间 调用和返回占时间 6、 在定义和使用宏调用时应该注意的问题 ① 参数多次计算。 ② 运算符优先级引起的问题。 ③ 宏定义中的字符串相连。 定义: # define square(x) x * x 调用: z = square( x + y ); 展开: z = x + y * x + y; { 运算符 “*” 的优先级高于 “+” 的优先级, 接开表达式中, 将首先计算 y * x, 与题意不符。应加括号该定义
8、为: #define square(x) ((x) * (x)) 7、 在定义无参宏时,如果“语言符号字符串”是一个常量,则相应的“宏名”就是一个符号常量。 # define EOF -1 /* 文件尾 */ # define NULL 0 /* 空指针 */ 8、 使用宏定义的优点 ① 可提高源程序的可维护性 ② 可提高源程序的可移植性 ③ 减少源程序中重复书写字符串的工作量 7.3 文件包含 一、概念 用 #include 开始的预处理命令称文件包含命令。文件包含是指,一个源文件可以将另一个源文件的全部内容包含进来。 二
9、一般格式 1、# include “文件名” 先在当前目录搜索, 再搜索标准目录, 可指定路径 2、# include <文件名> 直接按标准目录搜索 三、预处理过程 按规定方法寻找文件, 若找到, 用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译;若找不到, 则预编译出错,提示无法打开相应文件。 四、文件包含的优点 一个大程序, 通常分为多个模块,并由多个人分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构) 或函数,集中到一个单独的文件中。这样, 凡是要使用其中数据或调用其中函数的人,只要使用
10、文件包含, 将所需文件包含进来即可, 不必再重复定义它们, 从而减少重复劳动。 五、说明 ① # include 命令行应写在程序的开头。 ② 一条包含命令, 只能指定一个被包含文件。如果要包含 n 个文件, 则要用 n 条包含命令。 ③ 文件包含可以嵌套,即被包含文件中又包含另一个文件。 ④ 包含文件修改后, 包含该文件的源程序必须重新编译。 ⑤ 前面例子中使用 # include 命令包含的是系统标准库的各种头文件 (如 stdio.h、math.h), 这些头文件的主要内容是标准库函数的原型说明、一些系统使用的符号常量定义等。用 #include 命令将这种文件包含进程序
11、 相当于在源程序的前面书写了这些函数原型, 这对于保证编译程序能够正确地完成对标准库函数的有关调用是非常重要的。这正是使用 #include 命令并将 # include 命令写在程序开始处的理由。标准库的所有头文件在 TC 目录的 include 目录下, 读者可以打开此目录查看其中的内容,但千万不要试图修改它。 【例7.3】一个包含文件的实例 本例中, format.h 用 # define 定义了一组输出格式, 在随后的主程序中用这些格式输出若干个变量的值。编辑生成 format 上的方法与编辑普通 C 源程序一样, 保存文件时给它指定合适的名称和后缀就可以了。 文件 forma
12、t.h 如下: # define NL "\n" # dchne D "%d" # define D1 D NL # define D2 D D1 # define D3 D D2 # define D4 D D3 主函数如下: # include "format.h" main() { int a, b, c, d ; a = 1; b = 2; c = 3; d = 4; printf(D1,a); printf(D2, a, b); printf(D3, a, b, c);
13、 printf(D4, a, b, c, d); } 运行结果: 1 1 2 1 2 3 1 2 3 4 7.4 条件编译 一、概念 通常源程序中所有的行都参加编译,但有时希望源程序中的某些行只在满足一定条件下才进行编译, 有时又希望满足某一条件时对一组语句进行编译, 不满足这一条件时编译另一组语句, 这就是“条件编译”。 二、作用 划出源程序的一些片段, 使预处理程序可以根据一定条件确定保留或丢掉某个片段, 或确定从几个片段中选取哪一个片段保留下来。 三、三种使用格式 1、根据标识符是否用 #define 命令定义过来选择保留程序段1或程序段2。其
14、命令格式如下: # ifndef 标识符 程序段1 # else 程序段2 # endif # ifdef 标识符 程序段1 # else 程序段2 # endif 2、根据表达式值是否为真, 选择保留程序段1还是程序段2。其命令格式如下: # if 表达式 程序段1 # else 程序段2 # endif 四、 说明 l 条件编译的选取语句操作是在预处理阶段完成的,因此,预处理命令中的表达式都应该是预处理阶段可以求值的表达式。预处理完成之后,所有的预处理命令没
15、有了,得到的是预处理命令展开或选择好的结果。 l 采用条件编译, 可以减少被编译的语句, 从而减少目标程序的长度,缩短运行时间。 l 条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。 【例7.4】一个条件编译实例 调试程序时, 经常要打印一些跟踪信息, 调试结束后, 我们希望这些打印信息不再出现。但是, 这些信息对于我们将来修改程序、重新调试相当重要。本例使用条件编译处理这些打印行, 使用一个调试宏定义 DEBUG 来实现对调试行的选择控制。条件编译的形式如下: # define DEBUG 1 # if DEBUG …
16、 /* 此处为打印行或调试行 */
# endif
调试程序时, DEBUG 定义为1;调试结束, DEBUG 定义为 0。可在所有调试及打印行处加入本例的条件编译命令。
下面是一段示例程序:
# define DEBUG 1
# include
©2010-2025 宁波自信网络信息技术有限公司 版权所有
客服电话:4009-655-100 投诉/维权电话:18658249818