第10章预处理命令C语言的一个重要特征是它的预处理功能。我们知道,一个高级语言源程序在计算机上运行,必须先用编译程序将其翻译为机器语言。编译包括词法分析、语法分析、代码生成、代码优化等步骤,有时在编译之前还要做某些预处理工作,如去掉注释,变换格式等。C语言允许在源程序中包含预处理命令,在正式编译之前(词法分析之前)系统先对这些命令进行“预处理”,然后整个源程序再进行通常的编译处理。从语法上讲,这些预处理命令不是C语言的一部分,但使用它们却扩展了C语言程序设计的环境,可以简化程序开发过程,提高程序的可读性,也更有利于移植和调试C语言程序。本章主要介绍宏定义、文件包括和条件编译等预处理命令。源程序生成执行文件的过程:C语言源程序.c或.cpp编译目标程序.obj链接执行程序.exe#includestdio.h#definePI3.14159voidmain(){floatr=4;printf(“s=%f\n”,PI*r*r);}预处理命令预处理本章讨论之重点!10.1预处理命令简介预处理命令:C源程序中以#开头、以换行符结尾的行种类:宏定义#define、#undef文件包含#include条件编译#if、#ifdef、#else、#elif、#endif等其他#line、#error、#program等本章主要讨论的内容!格式:“#”开头占单独书写行语句尾不加分号10.2宏定义宏定义分为两种:不带参数的宏定义和带参数的宏定义。1、不带参数的宏定义一般形式#define标识符单词串指令名称宏名,被定义代表后面的单词串宏体,是宏的内容文本可缺省,表示宏名定义过或取消宏体功能用指定标识符(宏名)代替字符序列(宏体)如#defineYES1#defineNO0#definePI3.1415926#defineOUTprintf(“Hello,World”);#defineSIZE10#defineINT_STR%dvoidmain(){inta[SIZE],i;for(i=0;iSIZE;i++)scanf(INT_STR,&a[i]);for(i=SIZE-1;i=0;i--)printf(INT_STR,a[i]);}宏定义voidmain(){inta[10],i;for(i=0;i10;i++)scanf(%d,&a[i]);for(i=10-1;i=0;i--)printf(%d,a[i]);}预编译处理后注意:宏替换时仅仅是将源程序中与宏名相同的标识符替换成宏的内容文本,并不对宏的内容文本做任何处理。宏定义注意事项C程序员通常用大写字母来定义宏名,以便与变量名区别。如:#definePI3.14159宏定义的位置任意,但一般放在函数外面。宏定义时,如果单词串太长,需要写多行,可以在行尾使用反斜线“\”续行符例如:#defineLONG_STRINGthisisaverylongstringthatis\usedasanexample宏名的作用域是从#define定义之后直到该宏定义所在文件结束#undef可终止宏名作用域#undef标识符#defineYES1voidmain(){……..}#undefYES#defineYES0voidmax(){……..}YES原作用域YES新作用域宏定义可以嵌套定义,但不能递归定义#defineR2.0#definePI3.14159#defineL2*PI*R(√)#defineSPI*R*R(√)#defineMM+10(×)程序中字符串常量即双引号中的字符,不作为宏进行宏替换操作#defineXYZthisisatestprintf(XYZ);输出:XYZ,而不是:thisisatest。宏定义注意事项宏定义一般以换行结束,不要用分号结束,以免引起不必要的错误#definePI3.14;a=PI*2*2;预编译处理后a=3.14;*2*2;错误!宏可以被重复定义。#defineN10//第一次宏定义intf(){N的内容是10return(N*N);}#defineN20//第二次宏定义voidmain(){N的内容是20printf(%d\n,N+f());}在定义宏时,如果宏是一个表达式,那么一定要将这个表达式用()括起来,否则可能会引起非预期的结果。#defineNUM110#defineNUM220#defineNUMNUM1+NUM2voidmain(){inta=2,b=3;a*=NUM;b=b*NUM;printf(a=%d,b=%d\n,a,b);}voidmain(){inta=2,b=3;a*=10+20;b=b*10+20;printf(a=%d,b=%d\n,a,b);}预编译处理后输出结果:a=60,b=50()()()输出结果:a=60,b=902、带参数的宏定义一般形式#define标识符(参数列表)单词串参数表由一个或多个参数构成,参数只有参数名,没有数据类型符,参数之间用逗号隔开,参数名必须是合法的标识符通常会引用宏的参数例:#defineS(a,b)a*b………..area=S(3,2);宏展开:area=3*2;宏展开:形参用实参换,其它字符保留宏体及各形参外一般应加括号()不能加空格例#defineS(r)PI*r*r相当于定义了不带参宏S,代表字符串“(r)PI*r*r”例#definePOWER(x)x*xx=4;y=6;z=POWER(x+y);宏展开:z=x+y*x+y;一般写成:#definePOWER(x)((x)*(x))宏展开:z=((x+y)*(x+y));#defineMAX(x,y)(x)(y)?(x):(y)…….voidmain(){inta,b,c,d,t;…….t=MAX(a+b,c+d);……}宏展开:t=(a+b)(c+d)?(a+b):(c+d);intmax(intx,inty){return(xy?x:y);}voidmain(){inta,b,c,d,t;…….t=max(a+b,c+d);………}【例】用宏定义和函数实现同样的功能带参的宏与函数区别带参宏函数处理时间编译时程序运行时参数类型无类型问题定义实参、形参类型处理过程不分配内存,简单的字符置换分配内存,先求实参值,再代入形参程序长度变长不变运行速度不占运行时间调用和返回占时间10.3文件包含处理过程功能一个源文件可将另一个源文件的内容全部包含进来一般形式#include包含文件名或#include“包含文件名”预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译:直接到系统指定的“文件包含目录”去查找被包含的文件“”:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“文件包含目录”去查找。预编译处理后A源文件prg1.cpp#includeprg1.cppB源文件prg2.cppBA新源文件prg2.cpp文件包含举例:#includehead.h#includefunc.cppvoidmain(){inta,b,c;a=getnum();b=getnum();c=max(max(a,b),NUM);printf(MAX=%d\n,c);}(stdio.h文件中的内容)#defineNUM10intmax(intx,inty){return(xy?x:y);}intgetnum(){inta;scanf(%d,&a)return(a);}voidmain(){inta,b,c;a=getnum();b=getnum();c=max(max(a,b),NUM);printf(MAX=%d\n,c);}#includestdio.h#defineNUM10intmax(intx,inty){return(xy?x:y);}intgetnum(){inta;scanf(%d,&a)return(a);}预编译处理后文件包含的优点:一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中(如上例中的文件head.h和func.cpp)。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。文件包含的几点说明常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以.h(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。文件包含可以嵌套,即被包含文件中又包含另一个文件。10.4条件编译根据一定的条件去编译源文件的不同部分,这就是条件编译。1、#if~#endif形式格式:#if条件1程序段1#elif条件2程序段2……#else程序段n#endif常量表达式。通常会用到宏名,条件可以不加括号“()”#elif和#else可以没有#endif必须存在,它是#if命令的结尾如果条件1为真就编译程序段1,否则如果条件2为真就编译程序段2,…,如果各条件都不为真就编译程序段n。作用:#if和#elif常常与defined命令配合使用,defined命令的格式为:defined(宏名)或defined宏名功能:判断某个宏是否已经定义,如果已经定义,defined命令返回1,否则返回0。Defined命令只能与#if或#elif配合使用,不能单独使用。例如:#ifdefined(USA)的含义是“如果定义了宏USA”。例:下面的程序利用ACTIVE_COUNTRY定义货币的名称#defineUSA0#defineENGLAND1#defineFRANCE2#defineACTIVE_COUNTRYUSA#ifACTIVE_COUNTRY==USAchar*currency=“dollar”;//有效#elifACTIVE_COUNTRY==ENGLANDchar*currency=pound;#elsechar*currency=france;#endifvoidmain(){floatprice1,price2,sumprice;scanf(%f%f,&price1,&price2);sumprice=price1+price2;printf(sum=%.2f%s,sumprice,currency);}char*currency=“dollar”;voidmain(){floatprice1,price2,sumprice;scanf(%f%f,&price1,&price2);sumprice=price1+price2;printf(sum=%.2f%s,sumprice,currency);}预编译处理后2、#ifdef~#endif形式格式:#ifdef宏名程序段1#else程序段2#endif等价于“#ifdefined(宏名)”在#ifdef和#else之间可以加多个#elif命令如果宏名已被#define行定义,则编译程序段1,否则编译程序段2作用:#defineINTEGER#ifdefINTEGERintadd(intx,inty)//有效{return(x+y);}#elsefloatadd(floatx,floaty){return(x+y);}#endifvoidmain(){#ifdefINTEGERinta,b,c;//有效scanf(%d%d,a,b);printf(a+b=%d\n,add(a,b));#elsefloata,b,c;scanf(%f%f,a,b);printf(a+b=%f\n,add(a,b));#endif}intadd(intx,inty)//有效{return(x+y);}voidmain(){inta,b,c;//有