第7章 编译预处理Word下载.docx

上传人:b****5 文档编号:20036077 上传时间:2023-01-16 格式:DOCX 页数:11 大小:21.08KB
下载 相关 举报
第7章 编译预处理Word下载.docx_第1页
第1页 / 共11页
第7章 编译预处理Word下载.docx_第2页
第2页 / 共11页
第7章 编译预处理Word下载.docx_第3页
第3页 / 共11页
第7章 编译预处理Word下载.docx_第4页
第4页 / 共11页
第7章 编译预处理Word下载.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

第7章 编译预处理Word下载.docx

《第7章 编译预处理Word下载.docx》由会员分享,可在线阅读,更多相关《第7章 编译预处理Word下载.docx(11页珍藏版)》请在冰豆网上搜索。

第7章 编译预处理Word下载.docx

见下面例7.1

(4)对在字符串常量及用户标识符中与宏名相同的部分不作替换。

例如已定义

#defineL1234

在程序中有变量Length,其中L不作替换。

同样在printf(“L=”,…)中的L不作替换。

(5)宏定义的作用域:

从定义开始到程序结束。

(6)当宏定义在一行中写不下,换行时,需要在行尾加换行字符“\”。

下面的例子可以帮助我们理解以上说明:

【例7.1】使用宏定义的例子。

程序如下:

#include<

#defineR3.0/*定义宏R*/

#definePI3.1415926/*定义宏PI*/

#defineL2*PI*R/*可使用已定义的宏名定义新的宏*/

#defineSPI*R*R

voidmain()

{

printf("

L=%.2f\nS=%.2f\n"

L,S);

/*对字符串常量中的的宏名L和S不作替换*/

}

运行结果如下:

L=18.85

S=28.27

程序说明:

经过宏展开后,printf函数中的输出项L被展开为2*3.1415926*3.0,S被展开为3.1415926*3.0*3.0,printf函数调用语句展开为

printf(“L=%.2f\nS=%.2f\n”,2*3.1415926*3.0,3.1415926*3.0*3.0);

7.1.2带参数的宏定义

带参数的宏定义命令形式为:

#define宏名(形参表)字符串

形参表中的不同形参之间用逗号隔开,字符串中包含形参表中的参数。

在编译预处理时,将程序中该命令后所有与宏名相同的文本用字符串置换,但置换时字符串中的形参要用相应的实参置换。

例如:

#defineM(a,b)a*b

area=M(3,7);

在宏展开时会替换成:

area=3*7;

也就是说由实参3替换了形参a,由实参7替换了形参b。

【例7.2】分析下面程序的执行结果。

#include<

#defineM(x,y)(x*y)/*带参数的宏定义*/

{inta=2,b=3,c,d;

c=M(a,b);

/*宏展开后:

c=(a*b)*/

d=M(a+1,b+1);

/*宏展开后:

d=(a+1*b+1)*/

printf("

c=%d,d=%d\n"

c,d);

程序运行结果:

c=6,d=6

带参数的宏展开只是简单的替换,没有计算功能,这一点与函数不同。

在使用带参数的宏时,要注意括号的使用,请看下面的例子:

(1)宏定义:

#defineMU(x,y)x*y

宏调用:

6/MU(2+3,4+5)

宏展开:

6/2+3*4+5

(2)宏定义:

#defineMU(x,y)(x*y)

6/(2+3*4+5)

(3)宏定义:

#defineMU(x,y)(x)*(y)

6/(2+3)*(4+5)

(4)宏定义:

#defineMU(x,y)((x)*(y))

6/((2+3)*(4+5))

带参数的宏在程序中使用时,形式与函数相似,但是在本质上他们是完全不同的。

区别在于:

(1)在程序控制上,函数的调用需要进行程序流程的转移,而使用带参数的宏则仅仅是表达式的运算。

(2)带参数的宏一般是个表达式,所以它不象函数那样有固定的数据类型。

宏的数据类型可以说是它的表达式运算结果的类型,随着使用的实参数值的不同,运算结果呈现不同的数据类型。

(3)在调用函数时,对使用的实参有一定的数据类型限制。

而带参数的宏的实参可以是任意数据类型。

(4)函数调用时存在着从实参向形参传递数据的过程。

而带参数的宏不存在这种过程。

在程序中使用带参数的宏比调用函数可以得到较高的程序执行速度,但是在对源程序进行编译时花费的时间要更多一些。

(5)带参数的宏除了使用运算表达式定义外,还可以使用函数。

在标准函数库中经常使用这种方式。

(6)宏定义如果使用不当,可能会产生不易觉察的错误。

在程序设计时,经常把那些反复使用的运算表达式定义为带参数的宏。

这样一方面使程序更加简洁,另一方面可以使运算的意义更加明显。

定义带参数的宏时,对形式参数的数量没有限制,但是一般情况下以不超过三个为宜。

下面给出几个常用的带参数的宏定义:

#defineMAX(x,y)((x>

y)?

x:

y)求x和y中较大的一个

#defineABS(x)((x>

=0)?

-x)求x的绝对值

#definePERCENT(x,y)(100.0*x/y)求x除以y的百分数值

#defineISODD(x)((x%2==1)?

1:

0)判断x是否为奇数

上面给出的宏定义中,运算表达式里出现的形参都是单纯的形式。

在实际应用时,应该根据使用情况加上保证运算顺序的圆括号。

7.1.3终止宏定义

可以用#undef终止宏定义的作用域,一般形式为:

#undef宏名

#definePI3.14

#undefPI

以上PI的作用域从#definePI3.14命令行开始,到#undefPI命令行结束;

从#undef以后PI变成无定义,不再代表3.14了。

7.2文件包含

文件包含(fileinclusion)预处理命令有两种格式:

#include“文件名”

文件名>

功能:

在编译当前源文件之前,先执行在此文件中的文件包含命令#include,把命令中指定的另一个源文件的内容插入到该命令所在行开始的一段空间,从而使被插入的文件的内容成为当前源文件的一部分。

执行第1种格式(用双引号将文件名括起来)的命令时,若文件名与文件的路径是一起给出的,则预处理程序只在指定的目录中查找指定的被包含文件,若路径名未给出,则首先在当前目录中查找,如果没有找到,再到系统指定的标准库目录中去查找。

执行第2种格式(用尖括号将文件名括起来)的命令时,则只到系统指定的标准库目录中查找。

由此可知,当文件名不带路径时,使用第一种格式所包含的查找范围要广一些。

一般说,如果为调用库函数而用#include命令来包含相关的头文件,则用尖括号,以节省查找时间。

如果要包含的是用户自己编写的文件(这种文件一般都在当前目录中),一般用双引号。

若文件不在当前目录中,双引号内可给出文件路径。

使用文件包含的方法,可以减少重复性的劳动,有利于程序的维护和修改。

在程序设计时,可以把一批常用的符号常量、函数说明、宏定义及某些有用的数据类型说明和类型定义,组织在一个独立的文件中,在程序需要使用到这些信息时,可用#include命令把他们包含到所需的位置上去。

从而免去每次使用他们时都要重新定义或说明的麻烦。

而对这个文件中的符号常量、函数说明、宏定义修改时,也比较方便,当然它的内容一旦修改,所有引用它的文件就要作相应的修改。

在调用库函数时,一般要用#include命令把对应的头文件包含到引用库函数的程序中来,例如程序中要调用getchar函数,则应在该调用语句所在的文件的头部写上:

#include<

存放在头文件中的内容,是编译程序对有关的源程序进行编译时,需要用到的一些信息,这些信息一般是:

(1)符号常量,如在stdio.h文件中有:

#defineEOF-l

#defineNULL0

定义了宏名EOF和NULL,在程序被编译之前,程序中的标识符EOF则均被-1置换,标识符NULL均被数字0置换,然后才开始编译。

(2)函数声明,如在stdio.h文件中有printf函数的原型说明,math.h文件中含有各个数学函数的原型说明。

(3)宏定义,如在stdio.h中含有宏定义:

#definegetchar()fgetc(stdin)

(4)某些有用的数据结构声明和类型定义。

在使用#include命令时还要注意以下几点:

(1)每个#include命令只能包含一个文件。

(2)文件包含可以嵌套,即一个被包含的源文件可以包含另一个源文件。

【例7.3】一个程序写在多个源文件中的应用举例。

源文件ex703.c中的内容:

#include"

ex703_1.c"

ex703_2.c"

{printf("

%d\n"

fun1(3,5));

fun2(3,5));

源文件ex703_1.c中的内容:

intfun1(inta,intb)

{returna+b;

源文件ex703_2.c中的内容:

intfun2(inta,intb)

{returna*b;

在文件ex703.c中,文件包含预编译命令:

将其他两个文件中的内容插入到文件包含命令所在位置,经过编译预处理后,源文件ex703.c包含的内容如下:

……/*文件stdio.h所包含的内容*/

intfun1(inta,intb)/*文件ex703_1.c所包含的内容*/

intfun2(inta,intb)/*文件ex703_1.c所包含的内容*/

程序说明:

经过编译预处理后,在对文件ex703.c的编译过程中,C所处理的是一个完整的程序。

7.3条件编译

一般情况下,源程序中的所有行都要参加编译。

但特殊情况下可能需要根据不同的条件编译源程序中的不同部分,也就是说,源程序中的一部分内容只在满足一定条件才进行编译,或者,当条件成立时去编译一组语句,而当条件不成立时编译另一组语句,这就是“条件编译”。

条件编译命令有以下几种形式。

1.#ifdef标识符

程序段l

#else

程序段2

#endif

当指定的标识符在此之前已经被“#define”语句定义过,“程序段1”被编译,否则,“程序段2”被编译。

其中“#else”部分可以省略,即

#ifdef标识符

例如,在调试程序时,经常要输出一些所需的信息,一旦调试结束,这些信息就不再需要了。

一种处理方法可以采用条件编译来完成,只要在源程序中插入类似于以下的语句。

#ifdefDEBUG

printf(“a=%d,b=%d\n”,a,b);

#endif

只要在程序的起始处有以下命令行

#defineDEBUG

程序在运行中就会输出a、b的值,以便调试程序时分析。

只要程序中需要输出的地方,都可以使用相同的方法来输出所需的信息。

调试结束后,只要将“#defineDEBUG”语句删除即可,而不必去一一删除相关的“printf”语句,这样可以简化调试工作。

2.#ifndef标识符

当指定的标识符在此之前没有被“#deifne”语句定义过,“程序段1”被编译,否则,“程序段2”被编译。

类似于#ifdef,“#else”部分可以省略。

3.#if表达式

程序段1

#else

程序段2

如果指定的表达式的值为真(非0),则编译程序段1,否则编译程序段2。

习题

1.单项选择题

(1)以下叙述中正确的是()。

A)用#include包含的头文件的后缀不可以是“.a”

B)若一些源程序中包含某个头文件;

当该头文件有错时,只需对该头文件进行修改,包含此头文件所有源程序不必重新进行编译

C)宏命令行可以看做是一行C语句

D)C编译中的预处理是在编译之前进行的

(2)程序中调用了库函数exit,必须包含头文件()。

A)string.hB)stdlib.hC)ctype.hD)math.h

(3)下列宏定义命令中,哪—个格式是正确的()。

A)#definepi=3.14159;

B)definepi=3.14159

C)#definepi="

3.14159"

D)#definepi(3.14159)

(4)宏定义为#definediv(x,y)x/y;

语句“printf(“div(x,y)=%d\n”,div(x+3,y-3));

”作宏替换后为()。

A)printf(”x/y%d\n”,(x+3)/(y-3));

B)printf(”x/y=%d\n”,x+3/y-3);

C)printf(”div(x,y)=%d”,x+3/y-3;

);

D)printf(”x/y=%d\n”,x+3/y-3;

(5)定义带参数的宏计算两个表达式的乘积,下列定义中哪个是正确的()。

A)#definemult(u,v)u*vB)#definemult(u,v)u*v;

C)#definemult(u,v)(u)*(v)D)#definemult(u,v)=(u)*(v)

(6)程序中调用了库函数strcmp,必须包含头文件()。

A)stdlib.hB)math.hC)ctype.hD)string.h

(7)以下程序的输出结果是()。

#defineMIN(x,y)(x)<

(y)?

(x):

(y)

main()

{inti,j,k;

i=10;

j=15;

k=10*MIN(i,j);

k);

}

A)15B)100C)10D)150

(8)以下程序中的for循环执行的次数是()。

#defineN2

#defineMN+1

#defineNUM(M+1)*M/2

{inti;

for(i=1;

i<

=NUM;

i++);

A)5B)6C)8D)9

(9)以下程序的输出结果是()。

#include"

stdio.h"

#defineFUDGF(y)2.84+y

#definePR(a)printf("

%d"

(int)(a))

#definePRINT1(a)PR(a);

putchar('

\n'

{intx=2;

PRINT1(FUDGF(5)*x);

A)11B)12C)13D)15

2.填空题。

(1)定义—个宏,用于判断所给出的年份year是否为闰年:

#defineLEAP_YEAR(y)______________

(2)定义带参数的宏为

#definemax(a,b)((a)>

(b)?

(a):

(b))

对表达式max(a,max(b,max(c,d)))作宏替换为(用文字描述)_______________。

(3)定义一个带参数的宏,若变量中的字符为大写字母则转换成小写字母________。

(4)以下程序完成a、b的互换,补全程序。

_______________________________

{inta,b;

scanf("

%d,%d"

&

a,&

b);

___________________

%d,%d\n"

a,b);

3.编程题。

(1)输入两个整数,求它们相除的余数。

用带参数的宏来实现。

(2)输入5个数后,输出其中绝对值最小的数。

要求定义带参数的宏,计算三个数中绝对值最小的数。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 教学研究 > 教学反思汇报

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1