编译预处理(最终讲稿)_第1页
编译预处理(最终讲稿)_第2页
编译预处理(最终讲稿)_第3页
编译预处理(最终讲稿)_第4页
编译预处理(最终讲稿)_第5页
已阅读5页,还剩31页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第8章 编译预处理,河南理工大学,重点、难点,本章重点: 宏定义命令 条件编译命令 文件包含命令 本章难点: 带参宏定义 条件编译,内容提要,8.1 宏定义 8.1.1 无参宏定义 8.1.2 带参宏定义 8.2 条件编译 8.3 文件包含,所谓编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果和源程序一起进行编译,得到目标代码。 C语言提供的编译预处理命令主要有宏定义、条件编译和文件包含等三种。为了能够和一般C语句区别开来,编译预处理命令以“#”号开头。它占用一个单独的书写行,命令行末尾没有分号。,8.1 宏定义,宏定义是指将一个标识符(又称宏名

2、)定义为一个字符串(或称替换文本)。在编译预处理时,对程序中出现的所有宏名都用相应的替换文本去替换,这称为“宏替换”或“宏展开”。 在语言中,“宏定义”可分为无参宏定义和带参宏定义两种。,8.1.1 无参宏定义,无参宏定义即定义没有参数的“宏”,其一般形式为: #define 标识符 替换文本 其中#define表示该语句行是宏定义命令,“标识符”为所定义的宏名,习惯上宏名用大写字母表示;“替换文本”可以是常量、关键字、表达式、语句等任意字符串。在define、宏名和替换文本之间分别用空格隔开。 #define命令可以不包含“替换文本”,此时仅说明宏名已被定义,以后可以使用。 第2章介绍的符号

3、常量的定义就是一种无参宏定义。,例8-1 用无参宏定义计算,s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y) #define M (y*y+3*y) main() int s,y; printf(Please input a number: ); scanf(%d, 运行情况如下: Please input a number: 4 s=336,注意,在宏定义中替换文本(y*y+3*y)两边的括号不能少,否则会产生错误。如改为以下定义: #difine M y*y+3*y 则在宏展开时将得到下述语句: s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y; 显

4、然与原题意要求不符,计算结果当然是错 的。因此在进行宏定义时必须注意,应保证 宏代换之后不发生错误。,说明,(1) 习惯上宏名用大写字母表示,以便与变量名区别开。但这并非规定,也允许使用小写字母。 (2) 用替换文本替换宏名只是一种简单的直接替换,替换文本中可以包含任意字符,系统在进行编译预处理时对它不作任何检查。 例如:#define PI 3.l415926 (编译时才报错) (3) 宏定义不是声明或执行语句,在行末不要加分号,如果加上分号则连分号也一起替换。 (4) 一个#define只能定义个宏,且一行只能定义一个宏。若需要定义多个宏就要使用多个#define,并写在多行上。,(5)

5、宏定义时如果一行写不下,可用“”续行。 例如: #define PI 3.1415926 /*正确*/ #define PI 3.1415 926 /*正确*/ (6) 宏定义原则上可以出现在源程序的任何地方,但通常写在函数之外,其作用域为从宏定义命令起到源程序文件结束。如要终止其作用域可使用#undef命令,其用法为: #undef 标识符 如:#undef PI,(7) 宏名在源程序中若用双撇号括起来,则在编译预处理时不对其作宏替换。也就是说,宏名被双撇号括起来时,仅作为一般字符串使用。 例8-2 宏替换的选择性。 #define PI 3.1415926 main() printf(PI

6、 is %9.7f.n,PI); 程序运行结果为: PI is 3.1415926.,(8) 宏定义允许嵌套,在宏定义的替换文本中可以使用已经定义过的宏名。在宏展开时层层替换。 例如: #define PI 3.1415926 #define S PI*r*r /* PI是已定义的宏名*/ 对语句: printf(%f,S); 在宏替换后变为: printf(%f,3.1415926*r*r);,例8-3 用无参宏定义表示常用的数据类型和输出格式。,#define INTEGER int #define REAL float #define P printf #define D %dn #de

7、fine F %fn main() INTEGER a=5, c=8, e=11; REAL b=3.8, d=9.7, f=21.08; P(D F,a,b); P(D F,c,d); P(D F,e,f); ,程序运行结果为: 5 3.800000 8 9.700000 11 21.080000,8.1.2 带参宏定义,带参宏定义的一般形式为: #define 宏名(形参表) 替换文本 其中,形参表由一个或多个形参组成,各形参之间用逗号隔开,替换文本中通常应包括有形参。 引用带参宏的一般形式为: 宏名(实参表) 带参宏定义展开时,先把宏引用替换为替换文本,再将替换文本中出现的形参用实参代替

8、。,例8-4 用带参宏定义求两数中的大者,#define MAX(a,b) (ab)?a:b main() int x,y,max; printf(input two numbers(x,y): ); scanf(%d,%d, 程序运行情况如下: input two numbers(x,y): 5,6 max=6,注意!,(1) 在带参宏定义中,宏名与其后的左括弧“(”之间不得有空格,否则将变为无参宏定义。 例如把: #define MAX(a,b) (ab)?a:b 写为: #define MAX (a,b) (ab)?a:b 将被认为是无参宏定义,宏名是MAX,替换文本为(a,b) (ab

9、)?a:b。 对语句:max=MAX(x,y); 进行宏替换后,将变为: max=(a,b) (ab)?a:b(x,y);这显然是错误的。,(2)在带参宏定义中,替换文本中的形参通常要用括号括起来以避免出错。,例8-5 分别引用以下宏定义,求3*F(3+2)的值。 (A) #define F(x) x*x+x (B) #define F(x) (x)*(x)+(x) (C) #define F(x) (x*x+x) (D) #define F(x) (x)*(x)+(x) 解:表达式3*F(3+2)在分别引用以上4个宏定义后,其值为: (A) 因为宏定义只作为一种简单的字符替换,所以在引用(A

10、)中的宏定义后,表达式3*F(3+2)被替换3*3+2*3+2+3+2 (B) 80。表达式3*F(3+2)被替换为:3*(3+2)*(3+2)+(3+2) (C) 48。表达式3*F(3+2)被替换为:3*(3+2*3+2+3+2) (D) 90。表达式3*F(3+2)被替换为 3*(3+2)*(3+2)+(3+2) 由此可见,使用带参数的宏定义,替换文本中的括号位置不同,可以得出不同的结果。使用时一定要仔细考虑。,(3) 宏定义也可用来定义多个语句,在宏替换时,把这些语句都替换到源程序中。,例8-6 一个宏定义代表多个语句。 #define SSSV(s1,s2,s3,v) s1=l*w;

11、 s2=l*h; s3=w*h; v=w*l*h; main() int l=3,w=4,h=5,sa,sb,sc,vv; SSSV(sa,sb,sc,vv); printf(sa=%dnsb=%dnsc=%dnvv=%dn,sa,sb,sc,vv); 程序运行结果为: sa=12 sb=15 sc=20 vv=60,带参宏定义与函数的主要区别如下:,(1) 定义方式不同。带参宏使用预处理命令#define定义;而函数使用函数定义。 (2) 参数性质不同。带参宏的参数表中的参数不必说明其类型,也不分配存储空间;而函数参数表中的参数需说明其类型并在使用时为其分配存储空间。 (3) 实现方式不同。

12、宏展开是在编译时由预处理程序完成的,不占用运行时间;而函数调用是在程序运行时进行,需占用一定的运行时间。 (4) 参数传递不同。若实参为表达式,引用带参宏时只进行简单的字符替换,不计算实参表达式的值;而函数调用时,则先计算表达式的值,然后代入形参。 (5) 返回值不同。带参宏定义无返回值;而函数有返回值。,例8-7 带参宏定义的实参是表达式的情况,#define SQ(y) (y)*(y) main() int a,sq; printf(input a number: ); scanf(%d, 程序运行情况如下: input a number: 3 sq=16,“完全展开,直接代替”,例8-8

13、 函数与带参宏定义的进一步比较,#define SQ_MACRO(y) (y)*(y) main() int i=1; printf(SQ_fun:n); while(i=5) printf(%dn,SQ_fun(i+); i=1; printf(SQ_MACRO:n); while(i=5) printf(%dn,SQ_MACRO(i+); SQ_fun(int y) return(y)*(y); ,程序运行结果为: SQ_fun: 1 4 9 16 25 SQ_MACRO: 2 12 30,8.2 条件编译,条件编译是在编译源文件之前,根据给定的条件决定编译的范围。 一般情况下,源程序中所

14、有语句都参加编译。但有时希望在满足一定条件时,编译其中的一部分语句,在不满足条件时编译另一部分语句。这就是所谓的“条件编译”。 条件编译对于程序的移植和调试是很有用的。在一套程序要产生不同的版本(如演示版本和实际版本)、避免重复定义时往往使用条件编译。,条件编译的三种形式,(1) 第一种形式: #ifdef 标识符 程序段1 #else 程序段2 #endif 功能是:如果标识符是已被#define命令定义过的宏名,就对程序段1进行编译;否则对程序段2进行编译。,#ifdef 标识符 程序段 #endif,例8-9 根据需要设置条件编译,使之能控制对一些提示信息的输出。,#define DEB

15、UG main() int a=4; #ifdef DEBUG printf(Now the programmer is debugging the program.); #else printf(a=%d.,a); #endif 程序运行结果为: Now the programmer is debugging the program. 若没有第一行的宏定义命令,程序运行后会输出: a=4.,(2) 第二种形式: #ifndef 标识符 程序段1 #else 程序段2 #endif 功能是:如果标识符未被#define命令定义过的宏名,就对程序段1进行编译;否则对程序段2进行编译。,(3) 第

16、三种形式: #if 常量表达式 程序段1 #else 程序段2 #endif 功能是:如果常量表达式的值为真(非0),就对程序段1进行编译;否则对程序段2进行编译。,例8-10 设置一个开关,判断输入值是半径还是边长,实现求圆或正方形的面积。,#define R 1 main() float c,r,s; printf (input a number:); scanf(%f, #endif ,程序运行情况如下: input a number:3 area of round is: 28.274309 若程序的第一行改为: #define R 0 则程序运行情况如下: input a numbe

17、r:3 area of square is: 9.000000,8.3 文件包含,所谓文件包含是指在一个文件中包含另一个文件的全部内容,使之成为该文件的一部分。这相当于是两个文件的合并。文件包含由文件包含命令#include来实现,其一般格式为: #include /*格式一*/ 或 #include 文件名 /*格式二*/ 其中“文件名”是指被包含的文件,称为头文件。头文件必须是文本文件,如C语言源程序文件等。头文件常以“.h”为后缀(h为head的缩写),但也可以是“.c”或其他,甚至没有后缀也是可以的。,区别,格式一和格式二的主要区别是在存放头文件的路径上。 使用格式一时,预处理程序只在

18、系统规定的目录(include子目录)中去查找指定的头文件,若找不到,则出错,这称为标准方式。 使用格式二时,预处理程序先在当前工作目录中寻找指定的头文件,若找不到,再按标准方式去查找。,区别,一般来说,如果调用系统提供的标准库函数时使用格式一(库函数相关的头文件一般放在系统规定的目录),以节省查找时间。 如果要包含的是用户自己编写的头文件(这种头文件往往放在当前工作目录),则一般使用格式二。,区别,另外,格式一中只能写文件名及其后缀,不能含有其他成分,但格式二中的双撇号内可以含有路径,如: #include /*错误*/ #include C:TCF2.c /*正确*/,优点,使用文件包含的手段,可以减少重复性的劳动,有利于程序的维护和修改,同时也是“模块化”设计思想所要求的。 将那些公用的或常用的宏定义、函数原型、数据类型定义及全局变量的定义和声明等,组织在一些头文件中,在程序需要使用到这些信息时,就用#include命令把它们包含到所需的位置上去,从而免去每次使用它们时都要重新定义或声明的麻烦。,例8-11 用户头文件的编写和使用,L8_11.h文件源代码如下: #ifndef _L8_11_H #define _L8_11_H /*定义宏,以防止重复包含此头文件*/ #include #define ADD(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论