C语言程序设计课件:编译预处理_第1页
C语言程序设计课件:编译预处理_第2页
C语言程序设计课件:编译预处理_第3页
C语言程序设计课件:编译预处理_第4页
C语言程序设计课件:编译预处理_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

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

文档简介

理9.1宏定义9.2文件包含单元小结 9.1宏

9.1.1不带参数的宏定义

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

#define标识符字符串

其作用是用一个指定的标识符来代表一个字符串。

说明:其中的“#”表示这是一条预处理命令;“define”为宏定义命令;“标识符”为所定义的宏名;“字符串”可以是常数、表达式、字符串等。例如:

#definePI3.14159

上述命令的作用是用标识符PI代替常量3.14159。在编译预处理时,将程序中该定义以后的所有标识符PI都用3.14159代替。

【例9-1】

输入圆的半径,求圆的周长、面积和球的体积,并输出结果。要求使用不带参数的宏定义圆周率。#include<stdio.h>#definePI3.14159main(){ floatr,l,s,v; printf("请输入圆的半径:");

scanf("%f",&r); l=2*PI*r; s=PI*r*r; v=PI*r*r*r*4/3; printf("周长=%.2f,面积=%.2f,球体积=%.2f\n",l,s,v);}图9-1例9-1程序运行结果

下面是关于宏定义的几点说明:

(1)宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。

(2)宏定义是用宏名来代表一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替代。字符串可以包含任何字符,可以是常数,也可以是表达式。预处理程序对它不做任何检查。如果有错误,只能在已被宏展开后的源程序编译时被发现,例如:

#defineN100

inta[N];

上面的例子先用N来代表常数100,然后定义一个一维数组a,令其数组元素为100个。再例如:

#defineM3+4

s=5*M;

其中,计算之后s的值为19,即在实际的编译运行时执行的是s=5*3+4=19。这和下面的例子并不等价。

#defineM(3+4)

s=5*M;

此时,在程序运行时执行的是s=5*(3+4)=35。

(3)宏定义不是说明或语句,在行末不必加分号。如果加上分号,则连分号也一起置换。例如:

#defineN100;

inta[N];

经宏展开后,该语句如下:

#defineN100;

inta[100;];

显然出现了语法错误。

(4)宏名在源程序中若用引号括起来,则预处理程序不对其作宏置换。

#defineOK200

main()

{

printf(“OK”);

printf(“\n”);

}

上面定义宏名OK表示200,但在printf函数调用语句中OK被引号括起来,因此,不作宏置换。

(5)宏定义必须写在函数之外,其作用域为从宏定义命令起到源程序结束。如果要终止其作用域,可使用命令#undef撤销已定义的宏。例如:

#definePI3.14

#undefPI

在#undef命令行之后的范围,PI不再代表3.14。

(6)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时,由预处理程序层层置换。例如:

#definePI3.14

#defineSPI*Y*Y

printf(“%f”,S);

在宏展开后表示如下:

printf("%f",3.14*Y*Y);

(7)宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只进行字符替换,不分配内存空间。

在程序中使用宏定义,有以下几个优点:

(1)能提高源程序的可读性。例如,对于以下语句:

return(2.0*3.1415926*3.0);和return(3.1415926*3.0*3.0);

编程人员能否很快地看出其意义呢?显然不能。但如果写成下面的形式:

return(2.0*PI*RADIUS);和return(PI*RADIUS*RADIUS);

编程人员就能一眼看出,这是在计算圆周长和圆面积。

(2)能提高源程序的可移植性。如要将RADIUS的值由3.0修改为4.0,只要在#define命令中修改一处便可。而在不使用宏定义的文件中,则要将多处的3.0修改为4.0。9.1.2带参数的宏定义

C语言中允许宏带有参数。在宏定义中参数称为形式参数,在宏调用中的参数称为实际参数。对于带参数的宏,在调用时不仅要进行宏展开,而且还要进行参数替换。

带参宏定义的一般形式如下:

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

在字符串中含有各个形参,带参宏调用的一般形式如下:

宏名(实参表)

【例9-2】

用带参数的宏定义编程,输入圆的半径,计算圆的面积。

#include<stdio.h>

#definePI3.1415926

#defineS(r)PI*r*r

main()

{ floata,radius;

printf(“请输入圆半径:”);

scanf(“%f”,&radius);

a=S(radius);

printf(“圆的面积为:%.2f\n”,a);

}

图9-2例9-2程序运行结果

带参数的宏定义的几点说明:

(1)宏名和形参列表之间不能有空格出现。例如,把“#defineS(r)PI*r*r”写为“#defineS(r)PI*r*r”将被认为是无参数的宏定义,宏名S将代表字符串“(r)PI*r*r”。

(2)形式参数不分配内存单元,因此不必进行类型定义。而宏调用中的实参有具体的值,要用它们去置换形参,因此需要进行类型说明。

(3)在宏定义中,字符串内的形参通常用括号括起来以避免出错。

【例9-3】如果长方形的长原本是4,宽原本是3,现长增加2,宽增加1,编程计算长方形的面积。

#include<stdio.h>

#defineS(a,b)a*b

main()

{ intlen,wid,area;

printf("请输入增加的长和宽值:");

scanf("%d%d",&len,&wid);

area=S(4+len,3+wid);

printf("area=%d\n",area);

}图9-3例9-3程序运行结果

该程序在进行宏展开时,用4+len置换a,用3+wid置换b,再用a*b置换S(a,b),得到如下语句:

area=4+len*3+wid;

即area=4+2*3+1=11。

该结果显然不是预期的结果。在C语言中,带参数的宏定义在调用时,实参不会把值传递给形参,而只是做简单的替换。如果把宏定义中的参数加上括号,那结果就不一样了,即改写成如下形式:

#defineS(a,b)(a)*(b)

而其他不变,那么宏展开之后将变成如下形式:

area=(4+len)*(3+wid);

即area=(4+2)*(3+1)=24。

初学者容易把带参数的宏与函数混淆。确实它们之间有一些类似之处,在调用函数时也是在函数名后的括号中写出实参,也要求实参与形参的数目相等。但是,带参的宏定义与函数是不同的,其不同之处主要有以下几点:

(1)函数在调用时,先求出实参表达式的值,然后再代入形参。而使用带参的宏只是进行简单的字符替换。

(2)函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行的,在展开时并不分配内存单元,并且不进行值的传递处理,也没有“返回值”的概念。

(3)函数中的实参和形参都要定义类型,二者的类型要求一致,如果不一致,则应进行类型转换。而宏不存在类型问题,宏名无类型,它的形参也无类型,只是一个符号代码,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。例如:

#defineCLC_LANGUAGE (字符)

#defineX4.3

(数值)

其中,CL和X不需要定义类型,它们不是变量,在程序中凡遇CL均以C_LANGUAGE代之,凡遇X均以4.3代之,显然不需定义类型。对带参数的宏同样如此,例如:

#defineS(r)PI*r*r

其中,r也不是变量,如果在语句中有S(3.6),则展开后为PI*3.6*3.6,语句中并不出现r,当然也不必定义r。

(4)调用函数只能得到一个返回值,而用宏可以得到几个结果。

【例9-4】计算圆的周长、面积和圆球的体积。

#include<stdio.h>

#definePI3.1415926

#defineCIRCLE(R,L,S,V)L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R

main()

{ floatr,l,s,v;

scanf("%f",&r);

CIRCLE(r,l,s,v);

printf("r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\n",r,l,s,v);

}

经预编译宏展开后源程序变成如下形式:

#include<stdio.h>

main()

{ floatr,l,s,v;

scanf("%f",&r);

l=2*3.1415926*r;s=3.1415926*r*r;v=4.0/3.0*3.1415926*r*r*r;

printf("r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\n",r,l,s,v);

}

图9-4例9-4程序运行结果

实参r的值已知,可以从宏返回三个值:l、s、v。其实,这只不过是字符置换而己,即将字符r置换R,l置换L,s置换S,v置换V,而并未在宏展开时求出l、s、v的值。

(5)使用宏次数多时,宏展开后源程序变长,因为每展开一次都使程序变长,而函数调用则不使源程序变长。

(6)宏替换不占用运行时间,只占用编译时间,而函数调用则占用运行时间(分配单元、保留现场、值传递、返回)。

一般用宏来代表简短的表达式比较合适。有些问题,使用宏和函数都可以解决。例如:

#defineMAX(x,y)(x)>(y)?(x):(y)

main()

{ inta,b,c,d,t;

t=MAX(a+b,c+d);

}

赋值语句展开后如下:

t=(a+b)>(c+d)?(a+b):(c+d);

MAX不是函数,程序中只有一个main函数,在main函数中就能求出t的值。这个问题也可用函数的形式表示:

intmax(intx,inty)

{return(x>y?x:y);}

main()

{ inta,b,c,d,t;

t=max(a+b,c+d);

}

max是函数,在main函数中调用max函数求出t的值。

如果善于利用宏定义,可以实现程序的简单化。例如,事先将程序中的“输出格式”定义好,可以减少在输出语句中每次都要写出具体输出格式的麻烦。 9.2文件包含

文件包含是C编译预处理的另一个重要功能。文件包含是指一个源文件可以将另一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。

文件包含命令的一般形式如下:

#include“包含文件名”

#include<包含文件名>

以上两种格式的区别如下:

(1)使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的包含文件目录(由用户在配置环境时设置)中查找。

(2)使用尖括号:直接到系统指定的包含文件目录中查找(该方式为标准方式)。

一般来说,如果要包含库函数,宜用尖括号;如果要包含用户自己编写的文件,宜用双引号。大多数情况下,使用双引号比较保险。

【例9-5】编写程序,比较两个整数的值,求出其中较大的值,保存为文件“max.cpp”。

/*求两个整数中较大数的程序max.cpp*/

max(intx,inty)

{ intm;

m=x;

if(y>x)

m=y;

return(m);

}

【例9-6】利用例9-5中的文件,编写程序计算三个整数中最大的数,并输出该数。

#include<stdio.h>

#include“max.cpp”

main()

{ inta,b,c,z;

printf("请输入三个整数:");

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

z=max(a,b);

if(z<c)

z=c;

printf("最大数为:%d\n",z);

}

图9-5例9-6程序运行结果

文件包含命令的功能是把指定的文件插入到该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都要书写那些公用部分,从而避免重复编写,并减少出错。

下面是关于文件包含命令的几点说明。

(1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上,如图9-6所示。图9-6文件包含编译预处理

在进行编译预处理时,要对#include命令进行文件包含处理,即将file2.cpp的全部内容复制插入到“#include“f

温馨提示

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

评论

0/150

提交评论