预处理命令知识分享

上传人:yulij****0329 文档编号:137630490 上传时间:2020-07-10 格式:PPT 页数:56 大小:509.50KB
返回 下载 相关 举报
预处理命令知识分享_第1页
第1页 / 共56页
预处理命令知识分享_第2页
第2页 / 共56页
预处理命令知识分享_第3页
第3页 / 共56页
预处理命令知识分享_第4页
第4页 / 共56页
预处理命令知识分享_第5页
第5页 / 共56页
点击查看更多>>
资源描述

《预处理命令知识分享》由会员分享,可在线阅读,更多相关《预处理命令知识分享(56页珍藏版)》请在金锄头文库上搜索。

1、9.1宏定义 9.2“文件包含”处理 9.3条件编译 习题,第9章 预处理命令,ANSI C标准规定可以在C源程序中加入一些“预处理命令”(preprocessor DireCtiveS) ,以改进程序设计环境,提高编程效率。这些预处理命令是由ANSI C统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译(因为编译程序不能识别它们)。必须在对程序进行通常的编译(包括词法和语法分析、代码生成、优化等)之前,先对程序中这些特殊的命令进行“预处理”, 即根据预处理命令对程序作相应的处理(例如,若程序中用#define命令定义了一个符号常量A,则在预处理时将程序中所有的A都置换为指定的

2、字符串。若程序中用#inClude命令包含一个文件“StDio.h”,,则在预处理时将StDio.h文件中的实际内容代替该命令)。经过预处理后程序不再包括预处理命令了,最后再由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。现在使用的许多C编译系统都包括了预处理、编译和连接等部分,在进行编译时一气呵成。因此不少用户误认为预处理命令是C语言的一部分,甚至以为它们是C语句,这是不对的。必须正确区别预处理命令和C语句、区别预处理和编译,才能正确使用预处理命令。C语言与其他高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。,C提供的预处理功能主要有以下三种: 1. 宏

3、定义 2. 文件包含 3. 条件编译 分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。,例9.1 #define PI3.1415926 main() float l,s,r,v; printf(input raDiuS); scanf(%f, ,运行情况如下: input radius:4 l=25.1328 s=50.2655 v=150.7966 说明: (1) 宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。 (2) 使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。例如,如果不定义PI

4、代表3.1415926,则在程序中要多处出现3.1415926,不仅麻烦,而且容易写错(或敲错),用宏名代替,,简单不易出错,因为记住一个宏名(它的名字往往用容易理解的单词表示)要比记住一个无规律的字符串容易,而且在读程序时能立即知道它的含义,当需要改变某一个常量时,可以只改变#define命令行,一改全改。例如,定义数组大小,可以用 #define array- size 1000 int arrayarray-size; 先指定array-size代表常量1000,因此数组array大小为1000,如果需要改变数组大小,只需改#define行: #define array-size 500

5、 使用宏定义,可以提高程序的通用性。,(3) 宏定义是用宏名代替一个字符串,也就是作简单的置换,不作正确性检查。如果写成 #define PI 3.l4l59 即把数字1写成小写字母l,预处理时也照样代入,不管含义是否正确。也就是说预编译时不作任何语法检查。只有在编译已被宏展开后的源程序时才会发现错误并报错。 (4) 宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。如: #define PI 3.1415926; area=PI*r*r;,经过宏展开后,该语句为 area=3.1415926;*r*r; 显然出现语法错误。 (5) #define命令出现在程序中函数的外

6、面,宏名的有效范围为定义命令之后到本源文件结束。通常,#define命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。 (6) 可以用#undef命令终止宏定义的作用域。例如:,#define g 8.8 main() | G的有效范围 #undef g f1() 由于#undef的作用,使G的作用范围在#undef行处终止,因此在f1函数中,g不再代表8.8。这样可以灵活控制宏定义的作用范围。,(7) 在进行宏定义时,可以引用已定义的宏名,可以层层置换。 例9.2 #define R 3.0 #define PI3.1415926 #define L2*PI*R #define

7、 SPI*R*R main() printf(L=%fns=%fn,L,S); ,运行情况如下: L=18.849556 s=28.274333 经过宏展开后,printf函数中的输出项L被展开为2*3.1415926*3.0,S展开为3.1415926*3.0*3.0,printf函数调用语句展开为printf(L=%fns=%fn,2*3.1415926*3.0,3.1415926*3.0*3.0); (8) 对程序中用双括号括起来的字符串内的字符,即使与宏名相同,也不进行置换。如例9.2中的printf函数内有两个l字符,一个在双引号内,它不被宏置换,另一个在双引号外,被宏置换展开。,(

8、9) 宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。 9.1.2带参数的宏定义 不是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为 #define宏名(参数表)字符串 字符串中包含在括弧中所指定的参数。如: #defines(a,b)a*b area=s(3,2);,定义矩形面积S,a和B是边长。在程序中用了s(3,2),把3、2分别代替宏定义中的形式,图9.1,参数a、b,即用3*2代替s(3,2)。因此赋值语句展开为area=3*2; 对带参的宏定义是这样展开置换的:在程序中如果有带实参的宏(如S(3,2),则按#define命

9、令行中指定的字符串从左到右进行置换。如果串中包含宏中的形参(如a、b),则将程序语句中相应的实参(可以是常量、变量或表达式)代替形参,如果宏定义中的字符串中的字符不是参数字符(如a*b中的*号),则保留。这样就形成了置换的字符串,见图9.1。,例9.3 #definePI3.1415926 #defineS(r)PI*r*r main() float a,area; a=3.6; area=S(a); printf(r=%fnarea=%fn,a,area); 运行结果如下: r=3.600000 area=40.715038,赋值语句area=s(a);经宏展开后为 area=3.14159

10、26*a*a; 说明: (1) 对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。例9.3中语句中有S(a),在展开时,找到#define命令行中的S(r),将S(a)中的实参a代替宏定义中的字符串“PI*r*r”中的形参r,得到PI*a*a。这是容易理解而且不会发生什么问题的。但是,如果有以下语句:area=S(a+B);这时把实参a+B代替PI*r*r中的形参r,成为area=PI*a+B*a+B;请注意在a+B外面没有括弧,显然这与程序设计者的原意不符。原意希望得到,area=PI*(a+B)*(a+B);为了得到这个结果,应当在定义时,在字符串

11、中的形式参数外面加一个括弧。即#define S(r) PI*(r)*(r)在对S(a+B)进行宏展开时,将a+B代替r,就成了PI*(a+B)*(a+B)这就达到了目的。 (2) 在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字符串的一部分。例如,如果有 #define s (r)PI*r*r 被认为s是符号常量(不带参的宏名),它代表字符串“(r) PI*r*r”。如果在语句中有area=S (a); 则被展开为 area=(r) PI*r*r (a) 显然不对了。,有些读者容易把带参数的宏和函数混淆。的确,它们之间有一定类似之处,在引用函数时也是在函数名后

12、的括弧内写实参,也要求实参与形参的数目相等。但是带参的宏定义与函数是不同的。主要有: (1) 函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。例如上面的S(a+b),在宏展开时并不求a+B的值,而只将实参字符“a+b”代替形参r。 (2) 函数调用是在程序运行时处理的,分配临时的内存单元。而宏展开则是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。,(3) 对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换。而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的

13、字符即可。宏定义时,字符串可以是任何类型的数据。例如: #defineCHAR1CHINA(字符) #definea 3.6 (数值) CHAR1和a不需要定义类型,它们不是变量,在程序中凡遇CHAR1均以CHINA代之;凡遇a均以3.6代之,显然不需定义类型。同样,对带参的宏: #defineS(r)PI*r*r,r也不是变量,如果在语句中有S(3.6),则展开后为PI*3.6*3.6,语句中并不出现r。当然也不必定义r的类型。 (4) 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。 例9.4 #definePI3.1415926 #define CIRCLE(r,l,s,v)L=

14、2*PI*R;S=PI*R*R;=4.0/3.0*PI*R*R*R main() float r,l,s,v;,scanf(%f,,printf(r=%6.2f,l=%6.2f,S=%6.2f,v=%6.2fn,r,l,S,v); 运行情况如下: 3.5 r=3.50,l= 21.99,S= 38.48,v= 179.59 请注意,实参r的值已知,可以从宏带回3个值(l,S,v)。其实,只不过是字符代替而已,将字符r代替R,l代替L,S代替S,v代替,而并未在宏展开时求出l、S、v的值。,(5) 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。 (6)

15、宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。 一般用宏来代表简短的表达式比较合适。有些问题,用宏和函数都可以。如: #define MAX(x,y)(x)(y)?(x)(y) main() int a,b,c,d,t; ,t=MAX(a+b,c+d); 赋值语句展开后为 t=(a+b)(c+d)?(a+b)(c+d); 注意:MAX不是函数,这里只有一个main函数,在main函数中就能求出t的值。 这个问题也可用函数来求: int max(int x,int y) return(xy?xy); main(),int a,B,C,D,t; t=

16、max(a+B,C+D); max是函数,在main函数中调用max函数才能求出t的值。 请仔细分析以上两种方法。 如果善于利用宏定义,可以实现程序的简化,如事先将程序中的“输出格式”定义好,以减少在输出语句中每次都要写出具体的输出格式的麻烦。,例9.5 #define PR printf #define NL n #define D %D #define D1 D NL #define D2 D D NL #define D3 D D D NL #define D4 D D D D NL #define S %S main() int a,B,C,D; char string=CHINA; a=1;B=2;C=3;D=4;,PR(D1,a); PR(D2,a,B); PR(D3,a,B,C); PR(D4,a,B,C,D); PR(S,string); 运行时输出以下结果: 1 12 123

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 中学教育 > 教学课件 > 高中课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号