C语言的预处理器课件_第1页
C语言的预处理器课件_第2页
C语言的预处理器课件_第3页
C语言的预处理器课件_第4页
C语言的预处理器课件_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

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

文档简介

C语言的预处理器

编译预处理命令不是C语言的语句,它的作用只是告诉(命令)编译系统,在编译源程序之前对源程序进行某种预加工,而后再进行编译。所有的编译预处理命令都是以符号“#”开头,末尾不加分号。预处理命令可以用在程序的任何地方。本章主要介绍以下的C语言的编译预处理命令:宏定义的命令:#define,#undef;文件包含命令:#include;条件编译命令:#if_#else_#endif;

#if_#elif_#endif;

#ifdef_#else_#endif;

#ifndef_#else_#endif。

12.1宏定义和宏替换12.1.1不带参数的宏定义和引用

不带参数的宏定义的一般形式为:

#define宏名字符序列 宏定义的作用是,在对源程序编译之前,将程序中出现的所有的宏名用对应的字符序列来代替。这种替换称宏替换或宏引用。不带参数宏替换,通常的用途是,用宏名定义程序中的常量,如:

#defineFALSE0#defineWORDS"TurboC++"

通常把宏定义写在文件的开始部分,函数的外面,或写在包含文件(#include)中。

例12.1.1宏定义与宏替换应用。程序定义了一个宏名:SIZE,用来定义数组str的容量。程序中还使用了系统定义的一个宏名NULL(空字符),用于检查字符型数组中字符串的结束符。程序的功能是,将用户输入的字符串存入数组str,然后,变成大写字母将其输出。

程序如下:

#include"stdio.h"#defineSIZE80 /*宏定义SIZE*/main(){

inti;charstr[SIZE];/*宏SIZE的引用*/

printf("Enterastring:");gets(str);

for(i=0;str[i]!=NULL;i++) /*宏NULL的引用*/

printf("%c",toupper(str[i]));

printf("\n");return;}宏替换的另一个常见的应用,是语句串的替换。也就是用一个宏名代表一组C语句。编译时,凡是程序中遇到这个宏名,就用这些语句代替。这相当于调用无参数的函数。例12.1.2求1~num之间的质数的程序。程序中使用了如下的C语句宏定义:

#definePRIMEfor(i=2;i<n;i++)\if(n%i==0)prime=0;

程序如下:

#include"stdio.h"#definePRIMEfor(i=2;i<n;i++)\if(n%i==0)prime=0; /*宏定义*/

main(){

intn,i,prime,num,k=0;

printf("Enteranumber:");

scanf("%d",&num);

for(n=2;n<num;n++){prime=1;PRIME; /*宏替换*/

if(prime){

printf("%4d",n);if(!(++k%5))printf("\n");}}

printf("\n");return0;}

宏定义可以嵌套进行。也就是说,在定义宏时,可以引用已有定义的宏名,实现层层替换。例如:#definePI3.14159#defineSPI*r*r#defineL2*S/r

其中定义S时,使用了已定义的PI;定义L时,使用了已定义的S。例12.1.3应用上面嵌套结构的宏定义,计算圆的面积和周长。

程序如下:

#include"stdio.h"#definePI3.14159#defineSPI*r*r#defineL2*S/rmain(){

floatr;

printf("Enterr:");

scanf("%f",&r);

printf("r=%f\tS=%f\tL=%f\n",r,S,L);

return;}程序输出举例(带下划线的数据是用户输入的):

Enterr:1r=1.000000S=3.141590L=6.283180

需要注意的是,这个程序的语句

printf("r=%f\tS=%f\tL=%f\n",r,S,L);中,两次出现S和L:在格式字符串中出现了S和L,在输出项列表中也出现了S和L。C语言规定,宏名出现在字符串中时,将不看作是宏名,因而也不做宏替换。所以才有如上所示的输出。例12.1.4将宏用于输出语句中的例子。

#include"stdio.h"#definePRNprintf/*定义宏*/#defineD"%d\n"#defineF"%f\n"#defineS"%s\n"main(){

inti=10;floatf=20.0;charch[]="string.";

PRN(D,i);PRN(F,f);PRN(S,ch);

return;}程序的运行结果为:

1020.000000

string.12.1.2带参数的宏定义和引用带参数的宏定义的一般形式为:

#define宏名(形参表)字符序列

程序中引用宏名时,写入实参,就像函数调用那样。定义了宏名后,编译系统在开始编译源程序前,把程序中引用的宏替换成相应的一串符号,然后进行编译。在替换过程中用实参代替形参。

下面是一个带参数的宏定义的例子:

#defineMAX(a,b)((a)>(b))?(a):(b)

例12.1.5利用上面的带参数的宏定义,编写求两个数中的较大者的程序。程序如下:#include"stdio.h"#defineMAX(a,b)((a)>(b))?(a):(b) /*宏定义*/

main(){

inta,b,max;

printf("Enter2numbers:");

scanf("%d%d",&a,&b);

max=MAX(a,b); /*宏替换*/

printf("Themanximumnum=%d\n",max);

return0;}

也可以将程序中的两个语句:

max=MAX(a,b);

printf("Themanximumnum=%d",max);写成如下的一个语句:

printf("Themanximumnum=%d",MAX(a,b));

其效果是一样的。我们看到,这种宏替换很象调用函数。例如:

max=MAX(a,b);MAX(a,b);但它们的工作的机理是完全不同的,在概念更上不要混淆。它们之间的不同,体现在以下四个方面。(1)函数调用时,要计算实参并向形参传送。而宏中的参数,只是进行简单的替换。既没有向形参拷贝数据,也没有传送参数地址。(2)函数中的实参和形参有确定数据类型,并且两者是一一对应的。宏名没有数据类型。它只是个符号。其参数也只是个符号而已。引用时,代入指定的字符。宏定义的字符串可以是任何数据类型。(3)宏是在编译时进行替换的,不分配内存空间。而函数调用是在程序运行时处理的,并分配相应的临时的内存空间。

(4)宏替换是不占程序的运行时间的,只占编译时间。但宏替换增加程序代码长度。宏替换的次数越多,程序代码长度越长。函数调用要消耗程序的运行时间,但不使程序变长。灵活恰当地运用宏替换,可以使源程序显得简洁,增加源程序的可读性。例12.1.6编写程序,将例12.1.3中无参数宏定义改为带参数r的宏定义:

#defineS(r)PI*(r)*(r)#defineL(r)2*S(r)/(r)

程序如下:

#include"stdio.h"#definePI3.14159 /*定义不带参数的宏*/#defineS(r)PI*(r)*(r) /*定义带参数r的宏*/#defineL(r)2*S(r)/(r) /*定义带参数r的宏*/

main(){

floatr;

printf("Enterr:");

scanf("%f",&r);

printf("r=%f\tS=%f\tL=%f\n",r,S(r),L(r)); /*宏的引用*/

return;}在以上两个例子中,宏的形参都用圆括号括起来了。这不是绝对必要的。例如,例12.1.5中宏定义也可以写成下面的形式:

#definePI3.14159#defineS(r)PI*r*r#defineL(r)2*S(r)/r但是,对于实参为表达式的情况,上面参数不带括号宏定义,可能会给出错误的计算结果。设有r=3.0.a=1.0.b=2.0

根据上面的宏定义,则有:

S(r)=s(3)=28.27431,L(r)=l(3)18.84954

而参数为表达式时,

S(a+b)=S(3)=7.14159,L(a+b)=12.28318显然结果是错的。后者因为实参没有括号,所以计算过程是:

S(a+b)=3.14159×1+2×1+2=3.14159+2+2=7.14159

如果在宏定义里给参数r加上括号,则计算过程是:

S(a+b)=3.14159×(1+2)×(1+2)=3.14159×3×3=28.27431

结果是正确的。为了避免出现上述可能出现的错误,在定义带参数的宏时,最好给参数加上圆括号。

12.1.3取消宏定义#undef命令用于取消先前已定义的宏名。其一般形式为:

#undef

宏名

12.2文件包含所谓文件包含,是指一个源文件将另一个源文件包含到自己的文件之中。

文件包含命令有如下两种形式:

#include<文件名>#include"文件名"

包含命令的功能是,在编译预处理时,用命令指定的文件名的文本内容来替代该命令,使包含文件的内容成为本程序的一部分。

如果文件名用了双引号,则首先查找当前目录,若找不到该文件,则查找命令行定义的其他目录。如果仍找不到该文件,则查找系统定义的标准目录。如果文件名使用了尖括号,则编译器首先查找命令行指定的目录;如果找不到该文件,则查找标准目录,不查找当前工作目录。例12.2.1给定半径,计算圆的周长和圆面积。为了展示利用文件包含命令处理多文件程序的设计方法,我们设计三个如下的包含文件,或称头文件:头文件1名为myin1.h。文件内容包含如下两条:#include<stdio.h>#definePI3.14159 /*宏定义PI*/头文件2名为myin2.h。其内容是函数lr的定义:

floatlr(floatr) /*计算圆周长的函数*/{

return2*sr(r)/r;}

头文件3名为myin3.h。文件内容是函数sr的定义。

floatsr(floatr) /*计算圆面积的函数*/{

returnPI*r*r;}

设计一个含有上述三个头文件和主函数程序如下:#include"myin1.h"#include"myin3.h"#include"myin2.h"main(){floatx=3.0;

printf("L=%f\n",lr(x));

printf("S=%f\n",sr(x));}这里在文件myin3.h和文件myin2.h中定义的两个函数是嵌套的。即函数lr()的定义中引用着函数sr()。函数sr()定义中还引用着头文件myin1.h中的宏定义PI。因此,在主函数文件中,三个包含命令的文件书写顺序,必须是先myin1.h,然后myin3.h,最后是myin2.h。使用包含文件的结果,本例的源程序由四个文件组成:三个头文件和一个主程序文件。经过编译预处理后,三个头文件被“包含”到主程序文件中,成为程序的组成部分。图12.1给出本例包含文件处理的情形。

图12.1例12.2.1包含文件处理示意图12.3条件编译C语言的条件编译预处理命令就是用于有选择的编译源程序中某些部分的。条件编译主要用于调试程序。条件编译的另一个用途是,可以用源程序产生不同版本。

12.3.1#if_#endif类型的条件编译命令这是根据表达式的值进行条件编译的命令。该条件编译命令有以下三种不同形式。1.#if_#endif

此命令的一般形式为:

#if常数表达式程序段#endif其作用是,如果常数表达式为真,则编译从#if到#endif之间的程序段(活语句块);否则就不编译,跳过这段程序。2.#if_#else_#endif此命令的一般形式为:

#if常量表达式程序段1#else

程序段2#endif其作用是,如果常量表达式为真,编译程序段1的代码段;否则,编译程序段2的代码段。例12.3.1用户给定一整数MAX。当MAX大于或等于100时,编译语句:

printf("MAX>=100");

否则,编译语句:

printf("MAX<100");程序如下:

#include<stdio.h>#defineMAX120main(){#ifMAX>=100

printf("MAX>=100\n");#else

printf("MAX<100\n");

#endif

printf("MAX=%d\n",MAX);return0;}3.#if_#elif_#endif

这是用于多路选择的编译命令。它的一般形式为:

#if常量表达式1程序段1#elif

常量表达式2程序段2

……

……#elif

常量表达式n

程序段n

#endif

其作用是,如果常量表达式1为真,则编译程序段1;否则,如果常量表达式2为真,则编译程序段2;否则,如果常量表达式3为真,则编译程序段3;直到最后,如果常量表达式n为真,则编译程序段n。12.3.2#ifdef和#ifndef类型的条件编译命令这是根据标识符的定义与否进行条件编译的命令。标识符是用宏定义命令实现的。因此,也可以说,这种条件编译是根据宏名是否被定义决定某程序段是否进行编译。这种条件编译预处理命令有两种格式。1.#ifdef_#endif类型条件编译命令这种类型的命令有以下两种格式:第一种格式为:

#ifd

温馨提示

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

评论

0/150

提交评论