第九章编译预处理9.1宏定义9.2文件包含处理9.3条件编译9.4带参数的主函数概述在前面各单元中我们已经使用了部分以“#”符号开头的命令。例如,#define、#include等。这些命令是在源程序正式编译前进行处理的,称为“编译预处理”命令。编译预处理主要包括宏的处理、包含文件的处理、条件编译的处理等。宏定义1、不带参数的宏定义用一个指定的标识符(即名字)来代表一个字符串它的一般形式为#define标识符字符串这就是已经介绍过的定义符号常量。如:#definePI3.1415926#definePI3.1415926main(){floatl,s,r,v;printf(“inputradius:”);scanf(“%f”,&r);l=2.0*PI*r;s=PI*r*r;v=3.0/4*PI*r*r*r;printf(“l=%10.4f\ns=%10.4f\nv=%10.4f\n”,l,s,v);}例1:下面的程序使用了宏定义,给出其运行结果运行情况如下:inputradius:4l=25.1328s=50.2655v=150.79663、说明:(1)宏名一般习惯用大写字母表示,以便与变量名相区别。(2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。(3)宏定义是用宏名代替一个字符串,也就是作简单的置换,不作正确性检查。(4)宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。如:#definePI3.1415926;area=PI*r*r;经过宏展开后,该语句为area=3.1415926;*r*r;显然出现语法错误。(5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。通常,#define命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。(6)可以用#undef命令终止宏定义的作用城。例如:#defineG9.8main(){...}#undefGf1(){...}G的有效范围(7)在进行宏定义时,可以引用已定义的宏名,可以层层置换。#defineR3.0#definePI3.1415926#defineL2*PI*R#defineSPI*R*Rmain(){printf(“L=%f\nS=%f\n”,L,S);}例2分析下面程序的运行结果运行情况如下:L=18.849556S=28.274333(8)对程序中用双括号括起来的字符串内的字符,即使与宏名相同,也不进行置换。(9)宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。4、带参数的宏定义带参数的宏定义除了字符串替换之外,还要进行参数的替换。定义的一般形式为#define宏名(参数表)字符串字符串中包含在括弧中所指定的参数。如:#defineS(a,b)a*barea=S(3,2);例3:分析下面程序的运行结果#definePI3.1415926#defineS(r)PI*r*rmain(){floata,area;a=3.6;area=S(a);printf(“r=%f\narea=%f\n”,a,area);}运行结果如下:r=3.600000area=40.715038赋值语句area=S(a);经宏展开后为area=3.1415926*a*a5、说明:(1)对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。(2)在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字符串的一部分。例如,如果有#defineS(r)PI*r*r被认为S是符号常量(不带参的宏名),它代表字符串“(r)PIrr”。如果在语句中有area=S(a);则被展开为area=(r)PI*r*r(a)显然不对了。#definePRprintf#defineNL\n”#defineD”%d#defineE%d#defineD1DNL#defineD2DENL#defineD3DEENL#defineD4DEEENL#defineS“%s”main(){inta,b,c,d;charstring[]=“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);}例4:分析下面程序的运行结果:运行时输出以下结果:1121231234CHINA文件包含命令可以将另一个C源程序的全部内容包含进来,它可以避免不必要的重复劳动。文件包含处理1、一般形式:#include“文件名”或#include文件名2、例:可以将例4程序改为:(1)文件format.h#definePRprintf#defineNL\n”#defineD“%d#defineE%d#defineD1DNL#defineD2DENL#defineD3DEENL#defineD4DEEENL#defineS“%s”(2)文件file1.c#include“format.h”main(){inta,b,c,d;charstring[]=“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)#ifdef标识符程序段1#else程序段2#endif它的作用是当所指定的标识符已经被#define命令定义过,则在程序编译阶段只编译程序段1,否则编译程序段2。其中#else部分可以没有,即#ifdef标识符程序段1#endif(2)#ifndef标识符程序段1#else程序段2#endif它的作用是若标识符未被定义过则编译程序段1,否则编译程序段2。(3)#if表达式程序段1#else程序段2#endif它的作用是当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。例输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或改为小写字母输出。程序如下:#defineLETTER1main(){charstr[20]=“clanguage”,c;inti;i=0;while((c=str[i])!=‘\0’){i++;#ifLETTERif(c=‘a’&&c=‘z’)c=c-32;#elseif(c=‘A’&&c=‘Z’)c=c+32;#endifprintf(“%c”,c);}}运行结果为:CLANGUAGE条件编译带参数的主函数一般情况下,在DOS环境执行文件的形式为“c:\命令动词参数表”。其中参数表被称为命令行参数。其中,argc表示命令行参数的个数,指针数组argv[]分别指向命令行各参数的首地址。在编辑C语言源程序时,main()函数可带有两个形参,形式为:voidmain(intargc,char*argv[])例如:取名file.c的文件经过编译、连接后产生file.exe,在DOS提示符下输入如下字符串时:c:\fileparameter1parameter2↙则程序运行时,系统自动将命令行参数的个数3赋给argc。其中,argv[0]指向字符串“file”的首地址;argv[1]指向字符串“parameter1”的首地址;argv[2]指向字符串“parameter2”的首地址。如果文件名前有路径,它们都保存在argv[0]中。例:如下带参数的主函数#includestdio.hvoidmain(intargc,char*argv[]){inti;for(i=1;iargc;i++)printf(%s\n,argv[i]);}通过编译生成file可执行文件后,在DOS的提示符下输入filehelloworld,则程序输出为:helloworld1、给出下面程序宏替换后的结果。#definePI3.1415926#defineR3.0#defineL2*PI*R#defineSPI*R*Rmain(){printf(l=%f\ns=%f\n,L,S);}课堂练习答案:第一次替换:printf(l=%f\ns=%f\n,2*PI*R,PI*R*R);第二次替换:printf(l=%f\ns=%f\n,2*3.1415926*3.0,3.1415926*3.0*3.0);分析:学习宏替换要注意以下几点:⑴宏名一般习惯用大写字母,以便与变量名相区别;⑵宏替换由编译程序预先进行;宏名用作代替一个字符串,不作语法检查;⑶宏替换范围是除用双引号引起来的字符串以外的所有宏名字。(4)若替换后文本串中仍含有宏名字,将再次进行替换,直到程序中不含宏名字为止。(5)宏定义不是C语句,所以不能在行尾加分号。否则,宏展开时,会将分号作为字符串的1个字符,用于替换宏名。⑹C语言允许宏定义出现在程序中函数外面的任何位置,但一般情况下它总写在文件的开头。2.给出下面程序宏替换后的结果。#definePF(x)x*x/*#definePF(x)(x)*(x)*//*#definePF(x)((x)*(x))*/main(){inta=2,b=3,c;c=PF(a+b)/PF(a+1);printf(\nc=%d,c);}答案:(1)按第一种宏定义:c=a+b*a+b/a+1*a+1;(2)按第二种宏定义:c=(a+b)*(a+b)/(a+1)*(a+1);(3)按第三种宏定义:c=((a+b)*(a+b))/((a+1)*(a+1));分析:(1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。(2)有参宏的展开,只是将实参作为字符串,简单地置换形参字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整个字符串外,均加一对圆括号。4.输入一行字母,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。答案:#includestdio.h#defineLETTER1main(){charstr[20]=“CLanguage”,c;inti=0;while((c=str[i++])!=‘\0’){#ifLETTERif(c=‘a’&&c=‘z’)c=c-‘a’+‘A’;#elseif(c=‘A’&&c=‘Z’)c=c-‘A’+‘a’;#endifprintf(“%c”,c);}}