1、概概述述一、编译预处理的概念一、编译预处理的概念 C语言允许在程序中使用几种特殊的命令语言允许在程序中使用几种特殊的命令(它们不是一般的(它们不是一般的C语句),在语句),在C编译系统对编译系统对程序进行通常的编译之前,先对程序中这些程序进行通常的编译之前,先对程序中这些特殊命令进行特殊命令进行“预处理预处理”,然后将预处理的,然后将预处理的结果和源程序一起再进行通常的编译处理,结果和源程序一起再进行通常的编译处理,以得到目标代码。以得到目标代码。二、主要预处理功能二、主要预处理功能宏定义宏定义;文件包含;文件包含;条件编译条件编译10.1 10.1 文件包含文件包含 “文件包含文件包含“:是
2、指一个源文件可以将另外一是指一个源文件可以将另外一个个源文件的全部内容包含进来。源文件的全部内容包含进来。C语言提供语言提供#include命令来实现命令来实现“文件包文件包含含”的操作,其一般形式为:的操作,其一般形式为:#include“文件名”#include 或或作用:作用:使编译系统把指定的被包含文件嵌入使编译系统把指定的被包含文件嵌入到带有到带有#include的源文件中。的源文件中。“文件包含文件包含”示意图示意图file1.cfile2.cfile1.c包含包含#include“file2.c”BABA(a)(b)(c)假如假如file1.cfile1.c文件中的内容如下:文件
3、中的内容如下:int a,b,c;int a,b,c;float m,n,p;float m,n,p;char r,s,t;char r,s,t;file2.c file2.c文件的内容如下:文件的内容如下:#includefile1.cincludefile1.cmain()main()经过编译预处理后,经过编译预处理后,file2.cfile2.c文件的内容为:文件的内容为:int a,b,c;int a,b,c;float m,n,p;float m,n,p;char r,s,t;char r,s,t;main()main()包含文件的查找方法:#include“文件名文件名”先在当前工
4、作目录中去查找,若找不到再到指定的标准目录中去查找。如:对Turbo C编译系统,先在用户目录下 查找,然后在TCinclude文件夹中查找。#include 直接到系统指定的标准目录中去查找。如:对Turbo C编译系统,直接在TCinclude 文件夹中查找。在使用编译预处理在使用编译预处理#includeinclude语句时,需要注意的语句时,需要注意的几个问题如下:几个问题如下:(1)(1)当当#includeinclude语语句句指指定定的的文文件件中中的的内内容容发发生生改改变变时时,包包含含文文件件的的所所有有源源文文件件都都应应该该注注意意重重新新进进行行编编译等处理。译等处理
5、。(2)(2)文文件件包包括括可可以以嵌嵌套套使使用用,即即被被包包括括的的文文件件中中还还可以使用可以使用#includeinclude语句。语句。(3)(3)由由#includeinclude语语句句指指定定文文件件中中可可以以包包含含任任何何语语言言成成分分,通通常常将将经经常常使使用用的的、具具有有公公共共性性质质的的符符号号常常量量、带带参参数数的的宏宏定定义义以以及及外外部部变变量量等等集集中中起起来来放放在在这种文件中,这样可以避免一些重复操作。这种文件中,这样可以避免一些重复操作。(4)被包含的文件通常是源文件,而不是目标文件被包含的文件通常是源文件,而不是目标文件。根据经验的
6、总结,以下内容放在头文件中比较合根据经验的总结,以下内容放在头文件中比较合适。需要说明的是适。需要说明的是C语言对此没有强行的规定。语言对此没有强行的规定。包含指令(嵌套),如:包含指令(嵌套),如:#include函数声明,如:函数声明,如:externfloatfun(floatx);类型说明,如:类型说明,如:enumboolfalse,true常量定义,如:常量定义,如:constfloatpi=3.14159;数据声明,如:数据声明,如:externintm;宏定义,如:宏定义,如:#definePI3.1415926;10.2.1无参宏定义无参宏定义作用:作用:用标识符来代表一个字
7、符串。用标识符来代表一个字符串。10.2 10.2 宏定义宏定义宏名宏名宏内容宏内容无分号无分号宏宏:是对正文进行代入或嵌入的一种功能。即从是对正文进行代入或嵌入的一种功能。即从一字符流中取出某个字符串去代替源程序里一字符流中取出某个字符串去代替源程序里的标识符。的标识符。1.一般形式一般形式:#define define 标识符标识符 常量常量引例引例:#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
8、=%10.4fns=%10.4fnv=%10.4fn”,l,s,v);其中其中#definePI3.1415926作用是指定标识符作用是指定标识符PI来代表来代表“3.1415926”宏定义允许嵌套,在宏定义的字符串中可宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开中由预处以使用已经定义的宏名。在宏展开中由预处理程序层层代换。理程序层层代换。例如:例如:#defineN2#defineMN+1#defineNUM(M+1)*M/2替换的过程为;替换的过程为;NUM=(M+1)*M/2;而而M=M+1,也就是说,也就是说NUM=(N+1+1)*N+1/2。宏宏名用做代替一个字
9、符串,不作语法检查;名用做代替一个字符串,不作语法检查;宏定义的字符串不能以宏定义的字符串不能以“;”结尾,字符串结束后结尾,字符串结束后一一定要换行定要换行;C语言允许宏定义出现在程序中函数外面的任何语言允许宏定义出现在程序中函数外面的任何位置,但一般情况下它总写在文件的开头。位置,但一般情况下它总写在文件的开头。说明:说明:宏名一般习惯用大写字母,以便与变量名相区别;宏名一般习惯用大写字母,以便与变量名相区别;在进行宏定义时,可以引用已定义的宏名;在进行宏定义时,可以引用已定义的宏名;(6)宏名的前后应有空格,以便准确地辨认宏名,如果宏名的前后应有空格,以便准确地辨认宏名,如果没有留空格,
10、则程序运行的结果会出错。没有留空格,则程序运行的结果会出错。说明:说明:宏替换由编译程序预先进行;宏替换由编译程序预先进行;宏替换范围是宏替换范围是除字符串以外除字符串以外的所有宏名字;的所有宏名字;若替换后文本串中仍含有宏名字,将再次进若替换后文本串中仍含有宏名字,将再次进行替换,直到程序中不含宏名字为止。行替换,直到程序中不含宏名字为止。#define PI 3.1415926#define R 3.0#define L 2*PI*R#define S PI*R*R宏替换宏替换(宏展开宏展开):用宏内容(字符串)原样代:用宏内容(字符串)原样代换程序中的所有宏名字的过程。换程序中的所有宏名
11、字的过程。第一次替换:printf(l=%f ns=%fn,2*PI*R,PI*R*R);二:printf(l=%f ns=%fn,2*3.1415926*3.0,3.1415926*3.0*3.0);main()printf(l=%f ns=%fn,L,S);10.2.2有有参宏定义参宏定义一般形式一般形式:#define define 宏名(宏形参数表)宏名(宏形参数表)字符串字符串作用:作用:宏替换时以实参数替代形参数。宏替换时以实参数替代形参数。#define PI 3.1415926#define S(r)PI*r*rmain()float r1=3.6,area;area=S(r1
12、);/*S(r1)用PI*r1*r1替换*/printf(r=%f area=%fn,r1,area);注意:注意:宏替换后,程序的原意表达。宏替换后,程序的原意表达。#define PF(x)x*x /*#define PF(x)(x)*(x)*/*#define PF(x)(x)*(x)*/main()int a=2,b=3,c;c=PF(a+b)/PF(a+1);printf(nc=%d,c);按第一种宏定义:c=a+b*a+b/a+1*a+1;按第二种宏定义:c=(a+b)*(a+b)/(a+1)*(a+1);按第三种宏定义:c=(a+b)*(a+b)/(a+1)*(a+1);注意替换
13、时不求值,注意替换时不求值,只是字符串的原样替换只是字符串的原样替换#define MAX(x,y)xy?x:ymain()int n1,n2;float f1,f2;scanf(%d%d%f%f,&n1,&n2,&f1,&f2);printf(maxi=%dmaxf=%f,MAX(n1,n2),MAX(f1,f2);程序举例:程序举例:经预编译宏替换后的经预编译宏替换后的printf语句如下:语句如下:printf(maxi=%dmaxf=%f,n1n2?n1:n2,f1f2?f1:f2);10.2.3 10.2.3 终止宏定义终止宏定义 宏命令宏命令#undefundef用于终止宏定义的作
14、用域。一般形式为:用于终止宏定义的作用域。一般形式为:#unfine 宏名宏名例如:例如:#define area(r)(PI*r*r)main()#undef area(r)func()由于在函数由于在函数func()之前,使用之前,使用#undef终止宏名终止宏名area(r)的作的作用,在函数用,在函数func()中中area(r)不再起作用。不再起作用。#undef也可以用也可以用于函数内部。于函数内部。10.2.4带参数的宏替换与函数的主要区别带参数的宏替换与函数的主要区别函数调用时,先求出实参表达式的值,然后代入函数调用时,先求出实参表达式的值,然后代入 形参。而使用带参的宏只是进
15、行简单的字符替换。形参。而使用带参的宏只是进行简单的字符替换。函数调用是在程序运行时处理的,分配临时的内函数调用是在程序运行时处理的,分配临时的内 存单元。而宏替换则是在编译时进行的,在展开存单元。而宏替换则是在编译时进行的,在展开 时并不分配内存单元,不进行值的传递处理,也时并不分配内存单元,不进行值的传递处理,也 没有没有“返回值返回值”的概念。的概念。函数中函数名及参数均有一定的数据类型,而宏函数中函数名及参数均有一定的数据类型,而宏 不存在类型问题,宏名及其参数无类型。不存在类型问题,宏名及其参数无类型。宏替换不占运行时间,只占编译时间,而函数调宏替换不占运行时间,只占编译时间,而函数
16、调 用则占运行时间。用则占运行时间。例例宏替换与函数调用的区别。#define MUL(a,b)a+bint m(int a,int b)return(a*b);main()printf(“%dn”,MUL(1+2,5-4);printf(“%dn”,m(1+2,5-4);程序的运行结果为:73 原因显而易见,调用MUL宏时,计算的表达式是1+2*5-4,而调用m函数时,计算的表达式的是(1+2)*(5-4)。10.3 10.3 条条 件件 编编 译译 条件编译:根据条件选择被编译的源程序行。条件编译:根据条件选择被编译的源程序行。使用宏定义的标识符作为编译条件使用宏定义的标识符作为编译条件使
17、用常量表达式的值作为编译条件使用常量表达式的值作为编译条件一、使用宏定义的标识符作为编译条件一、使用宏定义的标识符作为编译条件#ifdef 标识符 程序段1#else 程序段2#endif 形式一:形式一:作用:作用:当所指定的标识当所指定的标识符已经被符已经被#define命令定命令定义过,则在程序编译阶义过,则在程序编译阶段只编译程序段段只编译程序段1,否则,否则编译程序段编译程序段2。#ifdef 标识符 程序段1#endif 形式二:形式二:作用:作用:当所指定的标识当所指定的标识符已经被符已经被#define命令定命令定义过,则在程序编译阶义过,则在程序编译阶段只编译程序段段只编译程
18、序段1,#ifndef 标识符 程序段1#else 程序段2#endif 形式三:形式三:作用:作用:当所指定的标识当所指定的标识符符未未被被#define命令定义命令定义过,则在程序编译阶段过,则在程序编译阶段只编译程序段只编译程序段1,否则编,否则编译程序段译程序段2。例1:#ifdef TURBO#define int int#else#define int short#endif可用于实现程序可用于实现程序在不同环境下的在不同环境下的兼容性。兼容性。例2:#ifdef DEBUG printf(“x=%d,y=%dn”,x,y);#endif可用于进行程序可用于进行程序的调试。的调试。
19、调试过程中,在程序前面加调试过程中,在程序前面加#defineDEBUG调试完成后,将前面的调试完成后,将前面的#defineDEBUG删除掉删除掉二、使用常量表达式的值作为编译条件二、使用常量表达式的值作为编译条件#if 表达式 程序段1#else 程序段2#endif 形式:形式:作用:作用:当所指定的表达当所指定的表达式为真(非零)时就编式为真(非零)时就编译程序段译程序段1,否则编译程,否则编译程序段序段2。可以事先给定一定条件,使程序在不可以事先给定一定条件,使程序在不同的条件下执行不同的功能。同的条件下执行不同的功能。注意注意:#if和和#endif必须配对使用。必须配对使用。带有
20、带有#elifelif的条件编译的条件编译定义的一般形式为:定义的一般形式为:#if if 表达式表达式1 1 程序段程序段1 1#elifelif表达式表达式2 2 程序段程序段2 2#elifelif表达式表达式3 3 程序段程序段3 3#elseelse 程序段程序段n n#endif#endif 这里的这里的#elif的含义是的含义是“elseif”。程序举例:用同一程序实现大小写字母转换程序举例:用同一程序实现大小写字母转换(若定义(若定义UP转换为大写)转换为大写)#include stdio.h#define UPmain()char s128;gets(s);#ifdef UP
21、 strupr(s);#else strlwr(s);#endifputs(s);例例 输入一个口令,根据需要设置条件编译,使之在输入一个口令,根据需要设置条件编译,使之在调试程序时,按原码输出;在使用时输出调试程序时,按原码输出;在使用时输出“*”号。号。#define DEBUGdefine DEBUGvoid main()void main()char pass80;int i=1;char pass80;int i=1;printf(nplease input password:);printf(nplease input password:);doi+;doi+;passi=getc
22、har();passi=getchar();#ifdef DEBUG#ifdef DEBUGputchar(passi);putchar(passi);#else#elseputchar(*);putchar(*);#endif#endifwhile(passi!=r);while(passi!=r);例例#ifdef和和#ifndef的使用。的使用。#defineTED10main()#ifdefTEDprintf(“hitedn”);#elseprintf(“hianyonen);#endif#ifndefRALPHprintf(“RALPHnotdefinedn”);#endif程序运行
23、结果为:程序运行结果为:hitedRALPHnotdefined10.4小小结结(1)C语言的宏定义可以分为两种形式:一种是带参数的语言的宏定义可以分为两种形式:一种是带参数的宏定义;另一种是不带参数的宏定义。宏定义;另一种是不带参数的宏定义。(2)(2)所谓文件包含预处理,是指在一个文件中将另外一所谓文件包含预处理,是指在一个文件中将另外一个文件的全部内容包含进来的处理过程,即将另外个文件的全部内容包含进来的处理过程,即将另外的文件包含到本文件中。的文件包含到本文件中。(3)一般情况下,源程序中所有的行都参加编译。但是一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只有在满足一定条件下有时希望对其中一部分内容只有在满足一定条件下才进行编译,也就是对一部分内容指定编译的条件,才进行编译,也就是对一部分内容指定编译的条件,这就是这就是“条件编译条件编译”。
©2010-2025 宁波自信网络信息技术有限公司 版权所有
客服电话:4008-655-100 投诉/维权电话:4009-655-100