内容正文:
教案用纸
第 8 周 总第 21 次 学时:2
教学班级: 课程:C语言程序设计 授课教师:
课题:5.7 编译预处理
教学方法:启发式、案例式
教具:常规教具
教学目标:使同学们掌握宏定义、文件包含及条件编译的格式及使用方法
教学重点:文件包含及条件编译的使用
教学难点:预定义文件与主函数不在同一个文件里如何运行
主要教学内容:
5.7 编译预处理
5.7.1 宏定义
5.7.2 文件包含
5.7.3 条件编译
课后回顾:
教案用纸
四个一:
提问:全局变量与局部变量的区别
导入:C程序的编译可分成编译预处理和正式编译两个步骤。在编译C源程序时,系统将自动调用编译预处理程序,根据编译预处理命令对程序进行适当的加工,处理完毕自动进入对源程序的正式编译。
正文:
5.7 编译预处理
C程序的编译可分成编译预处理和正式编译两个步骤。在编译C源程序时,系统将自动调用编译预处理程序,根据编译预处理命令对程序进行适当的加工,处理完毕自动进入对源程序的正式编译。预处理是C语言的一个重要功能,它由预处理程序负责完成。
C语言提供了多种预处理功能,如宏定义、文件包含和条件编译等。
预处理有以下几个特点。
(1)预处理命令均以#开头,结尾不加分号。
(2)预处理命令可以放在程序中任何位置,作用范围从定义到文件结尾。
5.7.1 宏定义
宏定义是用一个标识符(又称宏名)定义为一个字符串(又称宏体)。在编译预处理时,对程序中所有在宏定义中定义的标识符,都用宏定义中的相应字符串替换,称为“宏替换”或“宏展开”。
C语言的宏定义分为两种:一种是简单宏定义,即不带参数的宏定义;另一种是复杂的宏定义,即带参数的宏定义(有参宏定义)。
1.不带参数的宏定义
不带参数宏定义的一般形式如下:
(
#define
标识符 字符串
)
其中,#define是宏定义的命令,标识符和字符串之间用空格分开。标识符称为“宏名”,字符串又称为宏体。
功能:在程序中凡出现该标识符(宏名)的位置,经编译预处理的加工,都被替换成对应的宏体字符串,称之为“宏展开”。
例如:
#define PI 3.1415926 /*定义PI为一个宏,其值为3.1415926*/
关于宏的几点说明如下。
(1)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量,增加程序的可读性,而且不易出错。
(2)宏定义命令行放在源程序的函数外时,宏名的作用域从宏定义命令行开始到本源文件结束。
(3)宏名的作用域可以使用#undef命令终止,形式如下:
(
#undef
标识符
)
在#define语句定义了该宏之后,到#undef命令之前的程序中,该宏定义都有效,但在#undef命令后该宏则无效了。
(4)C语言中,用宏名替换一个字符串是简单的转换过程,不作语法检查。若将宏体的字符串中符号写错了,宏展开时照样代入,只有在编译宏展开后的源程序时才会提示语法错误。例如:
#define PI 3.141592B /*定义PI为一个宏,其值为3.141592B,值出错*/
预处理时照样替换,而不管其含义是否正确,一直到对宏展开的结果进行编译时,才会产生错误提示。
(5)一个宏名只能被定义一次,否则会出现重复定义的错误。
(6)宏定义可以嵌套。在宏体中,可以出现已定义的宏名,例如:
#define PI 3.1415926
#define PIR (PI*r) /*PI为已定义的宏名*/
(7)如果宏定义一行书写不下,可用反斜线“\”和回车键来结束本行,然后在下一行继续书写。例如,有如下程序:
#define STR "Hello,\
all the world people!"
main()
{ printf("%s
",STR); }
运行程序将输出:
Hello,all the world people!
(8)程序中出现的由双引号括起来的字符串,即使和宏名相同,也不进行宏替换。例如,在输出函数printf()中如果在双引号内有与宏名相同的字符串,也不认为是宏,只认为是普通字符串原样输出。
2.带参数的宏定义
带参数宏定义的一般格式如下:
(
#define
标识符(形参表) 形参表达式
)
其中,#define是宏定义的命令,标识符后的圆括号内为形参表,后面形参表达式为圆括号内的各形参构成的表达式。
例如:
#define MAX(a,b) (a>b)?(a):(b) /*定义了一个带参数的宏MAX,有两个参数a、b,其功能是求a和b中较大值*/
带参数的宏的调用格式如下:
(
标识符(实参表)
)
进行宏替换时,可以像使用函数一样,通过实参与形参传递数据。
带参数的宏展开是用宏调用提供的实参字符串,直接置换宏定义命令行中相应形参字符串,非形参字符保持不变。
带参数的宏展开时要注意以下两点。
(1)带参数的宏展开是按#define命令行中指定的字符串从左到右进行置换。
(2)如果宏体字符串中包含宏名中的形参,则将程序语句中相应的实参代替形参,如果字符串中的字符不是参数字符,则保留。
例如,在主函数中有如下语句:
int x=3,y=5,m;
m=MAX(x*2,y*3); /*使用带参数的宏,可以进行宏替换*/
则替换后相当于:
m=(x*2>y*3)?( x*2):( y*3); /*替换时将x*2替换形参表达式中的a,将y*3替换b*/
带参数的宏定义的几点说明如下。
(1)定义有参数的宏时,宏名应当与参数表的左括号紧紧相连。否则,C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。
(2)宏定义时,应将整个字符串以及其中的各个参数均用圆括号括起来,以确保宏展开后字符串中各个参数的计算顺序的正确性,避免出现错误。例如,宏定义为:
#define S(a,b) a*b
在程序中遇到如下语句:
m=S(a+1,b+1)*c;
对其进行宏展开如下:
m=a+1*b+1*c;
此时表达式变为a+b+c,这与想要的((a+1)*(b+1))*c不同,所以出错。可将每个参数和整个字符串都用括号括起来,改为以下宏定义:
#define S(a,b) ((a)*(b))
再对以上语句进行宏展开,结果如下:
m=((a+1)*(b+1))*c;
这正是想要的结果。
(3)在宏定义中的形参是标识符,而宏展开的实参可以是表达式。例如上面的语句:
m=S(a+1,b+1)*c;
在宏调用的语句S(a+1,b+1)中表达式a+1和b+1为S的两个实参。
5.7.2 文件包含
一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。
1.文件包含命令的两种格式
文件包含功能是把指定的一个源文件的全部内容插入到源程序该命令行。文件包含命令格式主要有以下两种。
(1)只检索C语言编译系统所确定的标准目录,格式如下:
(
#include
<文件名>
)
(2)首先对使用包含文件的源文件所在的目录进行检索,若没有找到指定的文件,再在标准目录中检索,格式如下:
(
#include
"
文件名
"
)
2.文件包含命令几点说明
(1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。
(2)常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。
(3)一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。
(4)文件包含可以嵌套,即被包含文件中又包含另一个文件。
5.7.3 条件编译
所谓条件编译,是指对源程序进行选择性编译。通常情况下,C语言程序的所有程序行都需要进行编译,但有时可能希望程序的某个程序段在满足一定条件时才决定进行编译或不进行编译。使用条件编译功能,为程序的调试和移植提供了有力的机制,使程序可以适应不同系统和硬件设置的通用性和灵活性。常用条件编译有以下两种形式,下面分别介绍。
1.#ifdef命令(或#ifndef命令)
#ifdef(或#ifndef)命令的一般格式如下
(
#
ifdef
标识符
#
if
n
def
标识符
程序段1
程序段1
[
#
else
或
[
#
else
程序段
2]
程序段
2]
#
endif
#
endif
)
功能:#ifdef命令的功能是如果“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。
#ifndef命令格式与#ifdef命令一样,功能与#ifdef命令相反。如果“标识符”未被#define命令定义过,则编译程序段1,否则编译程序段2。
2.#if命令
#if命令一般格式如下:
(
#
if
常量表达式
程序段1
[
#
else
程序段
2]
#
endif
)
功能:#if命令的功能是当表达式为非0(“逻辑真”)时,编译程序段1,否则编译程序段2。
3.条件编译和if语句的区别。
(1)if语句控制某些语句是否被执行,#if命令控制着某个程序段是否被编译。
(2)用if语句调试程序成功后,其调试语句仍被编译成目标代码,只是不再执行,成为废码。而使用条件编译调试程序成功后,其调试语句不再被编译,不生成目标代码,没有废码产生,空间利用率较高。
小结:C程序的编译可分为编译预处理和正式编译两个步骤。编译预处理是在将源程序生成目标文件之前,对源程序的加工。正确使用编译预处理命令可以有效的提高程序的开发效率。C语言提供了多种预处理命令,常用的有宏定义、文件包含、条件编译。
备 注
说明宏定义的作用,使同学们学习有目的性。
注意: 宏定义时定义要在main()函数之外。
注意:宏定义只是起到替换作用,并是影响运算过程。
重点:文件包含处理的运用。
难点:如果宏定义与主函数不在同一个文件里,则如何运行该程序。
难点:条件编译在实际中的应用。
注意:条件编译与条件语句的区别。
学科网(北京)股份有限公司
$$