《第5章编译预处理.ppt》由会员分享,可在线阅读,更多相关《第5章编译预处理.ppt(18页珍藏版)》请在三一文库上搜索。
1、5.2 宏 定 义,5.1 概 述,第五章 编译预处理,4.4 条 件 编 译,5.3 文 件 包 含,4.5 小 结,结 束,1了解预编译的概念,掌握宏定义的方法。 2了解“文件包含”与预处理的应用。 3了解条件编译的几种形式。, 5.1 概述,5.1 概述(P98) 编译预处理概念 编译预处理是指, 在进行编译之前, 先对源程序中的编译预处理命令进行处理; 然后再将处理的结果, 和源程序一起进行编译, 以得到目标代码 种类 宏定义 # define 文件包含 # include 条件编译 # if _ # else_ # endif 等 格式 “ # ” 开头 占单独书写行 语句尾不加分号
2、, 5.2 宏定义,5.2 宏定义(P98) 不带参数宏定义 一般形式: #define 宏名 字符串 功能:用指定标识符(宏名)代替字符序列(宏体) 定义位置:任意 (一般在函数外面) 作用域: 从定义命令到文件结束 # undef 可终止宏名作用域 格式: #undef 宏名 宏展开:预编译时, 用宏体替换宏名 - 不作语法检查 引号中的内容与宏名相同也不置换 宏定义可嵌套,不能递归 宏定义中使用必要的括号 ( ),如: #define YES 1 #define NO 0 #define PI 3.1415926 #define OUT printf(“Hello,World“);,可缺
3、省,表示宏名 定义过或取消宏体,如: # define YES 1 # define NO 0 if (x=YES) printf(“correct!n“); else if ( x=NO) printf(“error!n“); 展开后: if (x=1) printf(“correct!n“); else if (x=0) printf(“error!n“);,例: # define PI 3.14159 printf (“2*PI=%fn“ , PI*2 ); 宏展开: printf( “2*PI=%fn“, 3.14159*2 );,例: 不能递归 #define MAX MAX+10
4、(),例:宏定义可嵌套 # define WIDTH 80 # define LENGTH WIDTH+40 var = LENGTH * 2; 宏展开: var = 80 + 40 * 2;,( ),( ), 5.2 宏定义,【例 5.1】 给出下面宏替换的结果 # define R 5.0 # define FORMAT “Area =% fn“ # define PI 3.14159 # define AREA R*R*PI # define PR printf # include main() PR( FORMAT, AREA ); PR( “FORMAT“ ); ,宏替换的结果: #
5、 include main() printf( “Area =% fn“, 5.0*5.0*3.14.59 ); printf( “FORMAT“ ); ,运行结果: Area =78.539750 FORMAT, 5.2 宏定义,带参数宏定义 一般形式: # define 宏名(参数表) 宏体 宏展开:形参用实参换,其它字符保留 例: # define S( a, b ) a * b area = S( 3, 2 ); 宏展开: area = 3 * 2; 宏名与左圆括号之间不能留有空格 例: # define S(r) PI*r*r 相当于定义了不带参宏 S, 代表字符串 “(r) PI*
6、r*r” 宏体及各形参外一般应加括号 ( ),例: # 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);, 5.2 宏定义,【例 5.2】用带参数宏定义求两个数的最大值 # define MAX(x, y) (x)(y)?(x):(y) # include main() int a, b, c; float x, y, z; scanf(“%d%d“, ,宏展开: c = (a)(b)?(a):(b
7、),宏展开: z = (x)(y)?(x):(y),运行程序, 输入:3 87 12.5 23.8 输出结果为:c=87 z=23.799999, 5.2 宏定义,带参的宏与函数区别, 5.2 宏定义,在定义和使用宏调用时应该注意的问题 参数多次计算。 运算符优先级引起的问题。 宏定义中的字符串相连。 在定义无参宏时,如果“语言符号字符串”是一个常量,则相应的“宏名”就是一个符号常量。 # define EOF -1 /* 文件尾 */ # define NULL 0 /* 空指针 */ 使用宏定义的优点 可提高源程序的可维护性 可提高源程序的可移植性 减少源程序中重复书写字符串的工作量,定义
8、: # define min(A,B) (A)(B)?(A):(B) 调用1: z = min(x+y,x*y); 展开: z= (x+y)(x*y)?(x+y):(x*y); 调用2: z = min( n+ ,m+); 展开:z= (n+ )(m+)?(n+ ):(m+); 两个调用都出现参数表达式计算多次的问题,可能会成为程序中的隐含错误。,定义: # define square(x) x * x 调用: z = square( x + y ); 展开: z = x + y * x + y; 算符 “*” 的优先级高于 “+” 的优先级, 接开表达式中, 将首先计算 y * x, 与题意
9、不符。应加括号该定义为: #define square(x) (x) * (x),# define s1 “12345“ # define s2 “67890“ # define s s1 s2 /*命令连接 s1 和 s2, s 为“1234567890“, s1 和 s2 之间的空格可有多个 */ C 语言中,顺序书写的两个字符串间仅由空格、换行符或制表符分隔,编译时,自动将这两个字符串连接成一个长字符串。, 5.3 文件包含,5.3 文件包含(P101) 概念 用 #include 开始的预处理命令称文件包含命令。文件包含是指,一个源文件可以将另一个源文件的全部内容包含进来。 一般格式
10、# include “文件名” 或 # include 预处理过程 按规定方法寻找文件, 若找到, 用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译;若找不到, 则预编译出错,提示无法打开相应文件。,直接按标准目录搜索,先在当前目录搜索, 再搜索标准目录, 可指定路径,B,file2.c, 5.3 文件包含,文件包含的优点 一个大程序, 通常分为多个模块,并由多个人分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构) 或函数,集中到一个单独的文件中。这样, 凡是要使用其中数据或调用其中函数的人,只要使用文件包含, 将所需文件包含进来即可,
11、不必再重复定义它们, 从而减少重复劳动。 说明 # include 命令行应写在程序的开头。 一条包含命令, 只能指定一个被包含文件。如果要包含 n 个文件, 则要用 n 条包含命令。 文件包含可以嵌套,即被包含文件中又包含另一个文件。, 5.3 文件包含,包含文件修改后, 包含该文件的源程序必须重新编译。 前面例子中使用 # include 命令包含的是系统标准库的各种头文件 (如 stdio.h、math.h), 这些头文件的主要内容是标准库函数的原型说明、一些系统使用的符号常量定义等。用 #include 命令将这种文件包含进程序, 相当于在源程序的前面书写了这些函数原型, 这对于保证编
12、译程序能够正确地完成对标准库函数的有关调用是非常重要的。这正是使用 #include 命令并将 # include 命令写在程序开始处的理由。标准库的所有头文件在 TC 目录的 include 目录下, 读者可以打开此目录查看其中的内容,但千万不要试图修改它。, 5.3 文件包含,【例5.3】一个包含文件的实例 (P101) 本例中, format.h 用 # define 定义了一组输出格式, 在随后的主程序中用这些格式输出若干个变量的值。编辑生成 format 上的方法与编辑普通 C 源程序一样, 保存文件时给它指定合适的名称和后缀就可以了。 文件 format.h 如下: # defin
13、e NL “n“ # define 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); printf( D4, a,b,c,d); ,运行结果: 1 1 2 1 2 3 1 2 3 4,展开为: printf(“%d“ “%d“ “n“
14、 ,a,b);,连接为:“%d%dn“, 5.4 条件编译,5.4 条件编译(P102) 概念 通常源程序中所有的行都参加编译,但有时希望源程序中的某些行只在满足一定条件下才进行编译, 有时又希望满足某一条件时对一组语句进行编译, 不满足这一条件时编译另一组语句, 这就是“条件编译”。 作用 划出源程序的一些片段, 使预处理程序可以根据一定条件确定保留或丢掉某个片段, 或确定从几个片段中选取哪一个片段保留下来。, 5.4 条件编译,三种使用格式 根据标识符是否用 #define 命令定义过来选择保留程序段1或程序段2。其命令格式如下:,# ifdef 标识符 程序段1 # else 程序段2
15、# endif,# ifndef 标识符 程序段1 # else 程序段2 # endif,上面左右两种形式的选择方法正好相反。, 5.4 条件编译,根据表达式值是否为真, 选择保留程序段1还是程序段2。其命令格式如下:,# if 表达式 程序段1 # else 程序段2 # endif,根据多个表达式值, 从若干程序段中选取一段。其命令格式如下:,# if 表达式1 程序段1 # elif 表达式 2 程序段2 # elif 表达式3 # else 程序段n # endif, 5.4 条件编译,说明 条件编译的选取语句操作是在预处理阶段完成的,因此,预处理命令中的表达式都应该是预处理阶段可以
16、求值的表达式。预处理完成之后,所有的预处理命令没有了,得到的是预处理命令展开或选择好的结果。 采用条件编译, 可以减少被编译的语句, 从而减少目标程序的长度,缩短运行时间。 条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。, 5.4 条件编译,【例4.3】一个条件编译实例 (P103) 调试程序时, 经常要打印一些跟踪信息, 调试结束后, 我们希望这些打印信息不再出现。但是, 这些信息对于我们将来修改程序、重新调试相当重要。本例使用条件编译处理这些打印行, 使用一个调试宏定义 DEBUG 来实现对调试行的选择控制。条件编译的形式如下: # defin
17、e DEBUG 1 # if DEBUG /* 此处为打印行或调试行 */ # endif 调试程序时, DEBUG 定义为1;调试结束, DEBUG 定义为 0。可在所有调试及打印行处加入本例的条件编译命令。,下面是一段示例程序: # define DEBUG 1 # include main() # if DEBUG printf(“This is testing linen“); # endif printf(“This is normal linen“); ,宏名字用大写字母在源程序中非常醒目。,表达式, 4.9 小结,5.5 小结 本章内容不多但很实用, 出现的预处理命令有: define, include, if, elif, else, endif, ifdef, idndef。学习本章后应掌握的知识点如下: 宏定义的作用, 代参宏定义的定义方法, 宏调用和函数调用的区别, 宏展开的方法。 包含命令在多文件程序中的使用, 常用头文件及相关函数的使用。 条件编译的几种实现方法, 如何使用控制条件编译的宏定义。多文件程序的组织和实现方法。,
链接地址:https://www.31doc.com/p-2120897.html