第8章预处理命令_第1页
第8章预处理命令_第2页
第8章预处理命令_第3页
第8章预处理命令_第4页
第8章预处理命令_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

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

文档简介

第8章预处理命令本章概述本章的学习目标主要内容本章概述介绍宏定义的两种形式,介绍文件包含的使用方法,介绍条件编译的概念。第8章预处理命令本章的学习目标:

本章教学目的:掌握宏定义的两种形式,掌握文件包含的使用方法了解条件编译的概念。

本章教学重点:宏定义的两种形式,文件包含的使用方法。

本章教学难点:带参数的宏定义。第8章预处理命令第8章预处理命令8.1宏定义8.2“文件包含”处理8.3条件编译8.4本章小结主要内容:

编译预处理是指一些行首以#开头的特殊语句,必须在对程序进行通常的编译之前,先对程序中这些特殊的命令进行“预处理”,即根据预处理命令对程序作相应的处理(例如,若程序中用#define命令定义了一个符号常量A,则在预处理时将程序中所有的A都置换为指定的字符串)。

经过预处理后程序不再包括预处理命令了,最后再由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。

C语言与其它高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。第8章预处理命令C提供的预处理功能主要有以下三种:

宏定义、文件包含和条件编译它们分别用宏定义命令、文件包含命令和条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。8.1宏定义

宏定义指的是用#define定义的命令行,有不带参数和带参数两种形式。

8.1.1不带参数的宏定义

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

#define标识符

字符串

含义是用指定的宏名(即标识符)来代表其后字符串。

例如:

#defineSIZE10000

#definePI3.1415926

#defineFORMAT“%d,%d,%d\n”作用是指用标识符SIZE来代替字符串“10000”,用标识符PI来代替字符串“3.1415926”,用标识符FORMAT来代替字符串“

“%d,%d,%d\n”

”,

在编译预处理时,将程序中在该命令以后出现的所有的SIZE用10000代替、PI用3.1415926代替、FORMAT用“%d,%d,%d\n”代替。这种方法使用户能以一个简单的名字代替一个长的字符串,可以减小重复编程工作量,而且不容易出错。

把定义时所用的标识符称为“宏名”,即SIZE、PI和FORMAT都是宏名。在预编译时将宏名替换成字符串的过程称为“宏展开”。

注意:

宏名习惯用大写字母表示。定义宏与定义变量含义不同,宏定义只是作字符替换,并不给宏名分配内存空间。

例8.1使用宏来计算若干个数组元素的和。

#include<stdio.h>#defineSIZE100intmain(){inti,sum=0;intdata[SIZE];for(i=0;i<SIZE;i++)

{scanf(“%d”,&data[i]);

sum=sum+data[i];

}printf(“sum=%d\n”,sum);return0;}运行此程序可计算100个数组元素值的总和。对宏定义的说明:

(1)定义宏的目的是提高程序的可读性和通用性,便于程序的修改。例如若要把例8.1中数组data的元素个数改变为200,则只要将“#defineSIZE100”改为“#defineSIZE200”即可,程序中的其它语句均不用修改。

(2)不要在宏定义的行末加分号,因为宏定义不是C语句,加分号后,会将分号也作为字符串的组成部分,宏展开后可能出现错误。

(3)宏定义可以出现在程序的任何位置,一般位于文件开头,写在函数的外面。宏名的有效范围是从定义处到本源文件结束。可以用#undef命令终止宏定义的作用域。例如:

#definePI3.1415926

intmain()

{…

#undefPI

}

由于#undef的作用,使PI的作用范围在#undef行处终止。若在#undefPI之后再出现PI,则是无效的。

(4)宏定义是用宏名代替一个字符串,凡在宏定义有效范围内的宏名都用该字符串代替,但要注意:双引号内的与宏名相同的字符串不认为是宏名,不进行替换。例如:

#defineYES1

printf(“YES”);

程序将显示YES,而不是1。

(5)可以引用前面已经定义的宏名来定义新的宏,例如:

#defineI130

#defineI260

#defineJI1+I2

#defineKJ*2+J/2+I2这里J引用了I1和I2,K引用了J和I2。

注意K展开是:30+60*2+30+60/2+60,

不要以为是:(30+60)*2+(30+60)/2+60。

除非前面的定义是:#defineJ(I1+I2)。8.1.2带参数的宏定义

带参数的宏定义的一般形式为:

#define标识符(形参表)字符串

带参数的宏展开时,还要进行参数替换。宏定义中形参表中的形参,在程序中将用实参替换。例如:

#definePI3.14159

#defineV(r)4*PI*r*r*r/3

V(r)为带参数的宏,例如在程序中使用V(6)时,是用6代替宏定义中的形式参数r,V(6)展开为:4*3.14159*6*6*6/3,这是用来计算半径为6的球的体积。

例8.2使用带参数的宏计算梯形面积。

#include<stdio.h>

#defineS(a,b,h)(a+b)*h/2

intmain()

{intc1=6,c2=8,c3=10;

printf(“S=%d\n”,S(c1,c2,c3));

return0;

}S(c1,c2,c3)展开为:(c1+c2)*c3/2。程序实际执行的是下面的输出语句:printf(“S=%d\n”,(c1+c2)*c3/2);

如果将上面的S(c1,c2,c3)换成S(6,c2,2+8),运行程序后,输出结果还是70吗?

在使用带参数的宏定义时,宏名和括号之间不能有空格,否则系统会把括号、形参和字符串认为是一个字符串。

例如,如果有

#defineS(x,y)x*y

会被认为:

S是符号常量(不带参的宏名),它代表字符串“(x,y)x*y”。

上面介绍的用带参数的宏求球的体积和梯形的面积等问题显然也可以用函数解决。

带参数的宏和函数在形式上有相似的地方,但是它们有许多不同点:

(1)宏展开是在编译时进行的,不占用程序运行时间,在展开时并不分配内存单元,即使是带参数的宏也不分配内存单元;而函数调用则是在程序运行时进行处理的,占用程序运行时间,要为形参分配临时的内存单元。

(2)宏展开只是替换;而函数调用时,要计算实参表达式的值后传递给形参,不是替换。函数调用时存在着从实参向形参传递数据的过程,而使用带参数的宏,也不存在传递数据的过程。

(3)宏名以及它的参数都不存在类型问题,展开时用指定的字符串替换即可。而函数中的实参和形参都要定义类型。

(4)宏展开后对源程序长度有影响,而函数调用对源程序长度无影响。

有些问题,用宏和函数都可以,如下例:用函数:max(intx,inty){return(x>y)?x:y;}main(){inta,b,c,d,t;……t=max(a+b,c+d);……}用宏:#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);8.2“文件包含”处理

C语言提供了#include命令用来实现“文件包含”的操作。作用是将一个源文件的全部内容包含进另一个源文件中来。被包含的文件可以是C语言源文件、库函数头文件等。因为#include命令行通常都放在文件的开头,所以这些被包含的文件通常被称为“标题文件”或“头文件”,常以“.h”(h为head的缩写)为文件的扩展名。当然也可以用其他文件扩展名,但无论用什么扩展名,这个被包含文件必须是文本文件。

C集成环境为用户提供了很多库函数,每一个库函数都有自己对应的头文件,在C语言库函数与用户程序之间进行信息通信时,要使用一些库函数中定义的数据和变量,在使用某一库函数时,都要在程序中使用#include命令将该函数所对应的头文件包含进来,否则,程序在编译时报错。

文件包含的使用格式为:#include“文件名”

#include<文件名>

其中的“文件名”和<文件名>的区别是:当使用“文件名”

形式时,预处理程序首先检索当前文件目录是否有该文件,如果没有,再检索C编译系统中指定的目录;而使用<文件名>形式时,预处理程序直接检索C编译系统指定的目录。使用“文件名”形式时,文件名的前面可添加路径。例如:#include“d:\tc\include\stdio.h”

常用的标准库头文件的扩展名都是h,如:#include<stdio.h>/*标准输入输出函数文件*/#include<string.h>/*字符串函数文件*/#include<ctype.h>/*字符函数文件*/#include<math.h>/*数学函数库文件*/

“文件包含”命令可以节省程序设计人员的劳动。例如,可以将经常使用一组固定的符号常量(g=9.81,pi=3.1415926,e=2.718等等)用宏定义命令组成一个文件,只要用#include命令这个文件包含到自己所写的源文件中即可。正确的使用#include语句,将会减少不必要的重复工作,提高编程效率。特别是在一个软件开发小组共同协作开发大型软件时,include文件十分有用,利用它可以定义程序中共同的常量、函数原型、宏等,这样可以便于修改且不易出错。例8.3编制如下内容的被包含文件,该文件名为bj.h。#defineSWAP(x,y)t=x;x=y;y=t#defineMAX(x,y)x>y?x:y#defineNUM1(x,y)(x+y)/2#defineNUM2(x,y)sqrt(x+y)编写另一个程序file.c,内容如下:#include<stdio.h>#include<math.h>#include"bj.h"intmain(){floatt,x=567,y=123;printf("x=%f,y=%f\n",x,y);SWAP(x,y);printf("x=%f,y=%f\n",x,y);printf("x与y的最大值是%f\n",MAX(x,y));printf("x与y的算数平均值是%f\n",NUM1(x,y));printf("x与y的几何平均值是%f\n",NUM2(x,y));return0;}执行程序file.c,结果显示如下:doubleMAX=500.000000longMAX=37编译并执行程序file.c,结果如下:x=567.000000,y=123.000000x=123.000000,y=567.000000x与y的最大值是567.000000x与y的算数平均值是345.000000x与y的几何平均值是26.267851

注意:在编译时并不是作为两个文件进行连接的,而是作为一个源程序编译,得到一个目标(.obj)文件。说明:

(1)如果要包含n个文件,必须用n个include命令。即一个include命令只能指定一个被包含文件。

(2)假设“wj1.c”、“wj2.c”、“wj3.c”是三个不同的文件,若在“wj1.c”有如下两行命令:

#include<wj3.c>

#include<wj2.c>则在文件“wj1.c”中可以用“wj2.c”和“wj3.c”的内容,在文件“wj2.c”

中可以用“wj3.c”的内容,不必在文件“wj2.c”

中再使用“#include<wj3.c>”命令。

若在“wj1.c”

中只有“#include

<wj2.c>”

命令,而“wj1.c”中又要使用“wj3.c”的内容,也可以让“wj2.c”中出现“#include<wj3.c>”

命令。即文件包含可以嵌套使用。

例8.4分析下面程序的执行情况。/*file.c*/#include<stdio.h>#include"myfile.txt"intmain(){fun();return0;}myfile.txt文本文件的内容如下:voidfun(){charc;if((c=getchar())!='\n'){putchar(c);fun();}}

在编译file.c时,预处理过程中用myfile.txt文件的文本替换file.c中的#include“myfile.txt”,因此本例程序功能是接受用户的按键,直到按回车键为止,然后将字符序列显示出来。8.3条件编译有时希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句,使得同一个源程序在不同的编译条件下能够产生不同的目标代码文件。这就是“条件编译”。

条件编译命令有以下3种形式:1、#ifdef标识符

程序段1#else

程序段2#endif

作用:当标识符已经被定义过(一般是用define命令定义),则对程序段1进行编译;否则编译程序段2。

其中(#else程序段2)可以没有。作用:若标识符未被定义,则编译程序段1;否则编译程序段2。2、#ifndef标识符

温馨提示

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

评论

0/150

提交评论