《C语言程序设计09章预处理命令.ppt》由会员分享,可在线阅读,更多相关《C语言程序设计09章预处理命令.ppt(19页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1 1页页第九章第九章 预处理命令预处理命令概概 述述 ANSI C标标准准规规定定可可以以在在C源源程程序序中中加加入入一一些些“预预处处理理命命令令”(preprocessor directives),以以改改进进程程序序设设计计环环境境,提提高高编编程程效效率率。这这些些预预处处理理命命令令是是由由ANSI C统统一一规规定定的的,但但它它们们不不是是
2、C语语言言本本身身的的组组成成部部分分,更更不不是是C语语句句。编编译译程程序序不不能能识识别别预预处处理理命命令令,它它们们必必须须在在对对程程序序进进行行通通常常的的编编译译(包包括括词词法法和和语语法法分分析析、代代码码生生成成、优优化化等等)之之前前被被“预预处处理理”,即即在在编编译译前前先先根根据据预预处处理理命命令的要求对程序做出相应的处理。令的要求对程序做出相应的处理。经过预处理后,程序不再含有预处理命令了,然后再由编译经过预处理后,程序不再含有预处理命令了,然后再由编译程序对预处理后的源程序进行通常的编译处理,得到目标代码。程序对预处理后的源程序进行通常的编译处理,得到目标代
3、码。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 2 2页页 现在使用的现在使用的许多许多C编译系统编译系统都包括了都包括了预处理预处理、编译编译和和连接连接等部等部分,在进行编译时一气呵成。我们必须正确分,在进行编译时一气呵成。我们必须正确区别预处理命令和区别预处理命令和区别预处理命令和区别预处理命令和C C语语语语句句句句、区别预处理和编译区别预处理和编译区别预处理和编译区别预处理和编译,才能正确使用
4、预处理命令。,才能正确使用预处理命令。C语语言言与与其其它它高高级级语语言言的的一一个个重重要要区区别别是是可可以以使使用用预预处处理理命命令令和具有预处理的功能。和具有预处理的功能。C语言提供的语言提供的预处理功能预处理功能主要有以下三种:主要有以下三种:1.宏定义宏定义 2.文件包含文件包含 3.条件编译条件编译 分别用相应的宏定义命令、文件包含命令和条件编译命令来实分别用相应的宏定义命令、文件包含命令和条件编译命令来实现。为了现。为了与一般与一般C语句相区别语句相区别,这些命令以,这些命令以符号符号“#”开头开头。LanJ copyright LanJ copyright Monday,
5、January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 3 3页页9.1 9.1 宏定义宏定义9.1.1 不带参数的宏定义不带参数的宏定义 在在C语言中,我们可以定义一个指定的标识符来语言中,我们可以定义一个指定的标识符来代替代替程序程序中的一个字符串,这种定义称为中的一个字符串,这种定义称为“宏定义宏定义”,这个标识符(名,这个标识符(名字)称为字)称为“宏名宏名”。一般定义形式为:。一般定义形式为:#define 标识符标识符 字符串字符串 这就是已经介绍过的符号常量的定义。如:这就
6、是已经介绍过的符号常量的定义。如:#define PI 3.1415926 它的作用是:在编译预处理时,将程序中在该命令以后它的作用是:在编译预处理时,将程序中在该命令以后出现的所有的标识符出现的所有的标识符PI都都替换替换为为3.1415926这个这个字符串字符串。在预。在预编泽时,将宏名替换成字符串的过程称为编泽时,将宏名替换成字符串的过程称为“宏展开宏展开”。define是是宏定义命令宏定义命令。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科
7、学系 C C语言程序设计语言程序设计 第第 4 4页页例例9.1#define PI 3.1415926main()float l,s,r,v;printf(input radius:);scanf(%f,&r);l=2.0*PI*r;s=PI*r*r;v=3.0/4*PI*r*r*r;printf(l=%10.4fns=%10.4fnv=%10.4fn,l,s,v);LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设
8、计 第第 5 5页页说明:说明:1)宏名一般宏名一般约定约定用大写字母表示,以便与变量名相区别。用大写字母表示,以便与变量名相区别。2)使用宏定义,可以提高程序的通用性,能作到使用宏定义,可以提高程序的通用性,能作到“一改俱改一改俱改”。3)宏宏定定义义是是用用宏宏名名代代替替一一个个字字符符串串,只只作作简简单单的的替替换换操操作作,不不作作正正确确性性检检查查。如如果果词词义义或或语语义义错错误误,只只有有在在编编译译已已被被宏宏展展开开后后的源程序时才会发现错误并报错。例如:的源程序时才会发现错误并报错。例如:#define PI 3.14l59 把把第第二二个个“1”写写成成“l”。这
9、这样样的的错错误误在在预预处处理理时时是是不不会会被被发发现的,只有在编译时才会被发现井报错。现的,只有在编译时才会被发现井报错。4)宏宏定定义义不不是是C语语句句,不不要要随随便便在在行行末末加加分分号号。如如果果加加了了分分号号,则则会连同分号一起进行替换。会连同分号一起进行替换。5)#define命命令令出出现现在在程程序序中中函函数数的的外外面面,宏宏名名的的有有效效范范围围为为宏宏定定义义之之后后到到本本 源源文文件件末末。通通常常,#define命命令令写写在在文文件件开开头头,函函数之前,作为文件一部分,在此文件范围内有效。数之前,作为文件一部分,在此文件范围内有效。LanJ c
10、opyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 6 6页页6)可用可用#undef 命令来终止宏定义的作用域。这样可以灵活控制命令来终止宏定义的作用域。这样可以灵活控制宏定义的作用范围。宏定义的作用范围。7)在进行宏定义时,又可引用已定义的宏名,实现层层置换。在进行宏定义时,又可引用已定义的宏名,实现层层置换。8)对程序中用双引号括起来的字符串内的字符,即使与宏名相对程序中用双引号括起来的字符串内的字符,即使与宏名相同
11、,也不进行置换。如下例同,也不进行置换。如下例printf 函数的格式控制串中的函数的格式控制串中的L和和S字符,它们不被置换。字符,它们不被置换。例例9.2#define R 3.0#define PI 3.1415926#define L 2*PI*R#define S PI*R*R main()printf(“L=%fnS=%fn”,L,S);运行结果为:运行结果为:L=18.849556S=28.274333printf(“L=%fnS=%fn”,2*3.1415926*3.0,3.1415926*3.0*3.0);LanJ copyright LanJ copyright Monda
12、y,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 7 7页页9.1.2 9.1.2 带参数的宏定义带参数的宏定义 带参数的宏定义也是一种替换操作,但它要进行两次替带参数的宏定义也是一种替换操作,但它要进行两次替换(换(宏名字符串被简单替换宏名字符串被简单替换和和实参字符串简单替换形参实参字符串简单替换形参)。)。其定义的一般形式为:其定义的一般形式为:#define 宏名宏名(参数表参数表)字符串字符串 字符串中包含有参数表中所指定的参数。例如:字符串中包含有参数表中所指定的参
13、数。例如:#define S(a,b)a*b area=S(2,3);其中其中S(2,3)相当与相当与2*3。带参宏定义的置换过程:带参宏定义的置换过程:在程序中如果有带实参的宏在程序中如果有带实参的宏(例如例如S(3,2),则按,则按define命令行中指定的字命令行中指定的字 符串从左到右符串从左到右进行置换;进行置换;如果字符串中含有宏中的形参如果字符串中含有宏中的形参(如如a,b),则将,则将相应的实参字符串(可以是常量、变量或表达式)代替形参;相应的实参字符串(可以是常量、变量或表达式)代替形参;如果字符串中的字符不是参数字符如果字符串中的字符不是参数字符(如上例中如上例中*),则原
14、样,则原样保留。这样,便形成了置换的字符串。保留。这样,便形成了置换的字符串。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 8 8页页例例9.3#define PI 3.14#define S(r)PI*r*rmain()float a,area;a=3.6;area=S(a);printf(“r=%fnarea=%fn”,a,area);说明:说明:1)对带参数的宏的展开只是将宏名后面括号内的对带参数的
15、宏的展开只是将宏名后面括号内的实参字符串实参字符串代替代替define命令行中的命令行中的形参形参。如果有以下语句。如果有以下语句:area=S(2+3);与与 3.14*a*a 相同相同 LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 9 9页页再相应的宏展开形式为:再相应的宏展开形式为:area=3.14*5*5;area=3.14*(2+3)*(2+3);area=3.14*2+3*2+3;请注意请注
16、意在在a+b外面没有括弧,显然这与程序设计者的原意不外面没有括弧,显然这与程序设计者的原意不符。应当在定义时,在字符串中的形式参数外面加一个括弧。符。应当在定义时,在字符串中的形式参数外面加一个括弧。即即#define S(r)PI*(r)*(r)只有这样才会得到:只有这样才会得到:area=PI*(2+3)*(2+3);2)宏定义时,在宏名与带参数的括弧之间不应加空格,否则宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为宏名所代替的字符串。将空格以后的字符都作为宏名所代替的字符串。说明一点:说明一点:P192-193中的例中的例9.4和例和例9.5,只要求大家理解就行
17、,只要求大家理解就行了,这两种用法均无实际意义,我个人不提倡这种用法。了,这两种用法均无实际意义,我个人不提倡这种用法。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1010页页带参数的宏带参数的宏和和函数函数的比较:的比较:相似之处:在引用函数时,也是在函数名后的括弧内写实参,也要相似之处:在引用函数时,也是在函数名后的括弧内写实参,也要求实参与形参的数目相等,但这只是表面、形式上的相似。求实参与形参的
18、数目相等,但这只是表面、形式上的相似。带参的宏定义与函数的区别:带参的宏定义与函数的区别:1)函数调用时,函数调用时,要先求解实参表达式的值要先求解实参表达式的值,然后代入给形参;,然后代入给形参;而使用带参的宏,只是进行简单的字符替换。而使用带参的宏,只是进行简单的字符替换。2)函数调用是函数调用是在程序运行时在程序运行时处理的,要分配内存单元(栈);处理的,要分配内存单元(栈);而宏展开是而宏展开是在预处理时在预处理时进行的,在展开时并不分配内存单元,进行的,在展开时并不分配内存单元,不进行值的传递处理,更没有不进行值的传递处理,更没有“返回值返回值”的概念。的概念。这是两者最本质的区别。
19、这是两者最本质的区别。3)函数中的实参和形参都要定义类型,二者的类型要求函数中的实参和形参都要定义类型,二者的类型要求一一致,不一致,应进行类型转换;而宏不存在类型问题,宏名致,不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是代表一个符号。无类型,它的参数也无类型,只是代表一个符号。4)每进行一次宏展开都会使源程序增长,而函数调每进行一次宏展开都会使源程序增长,而函数调 用不会。用不会。5)宏替换不占运行时间,只占预处理时间。而函数调用则要宏替换不占运行时间,只占预处理时间。而函数调用则要占运行时间(分配单元、保现场、值传递、返回)。占运行时间(分配单元、保现场、
20、值传递、返回)。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1111页页9.2“9.2“文件包含文件包含”处理处理 所所谓谓“文文件件包包含含”处处理理是是指指一一个个源源文文件件将将另另外外的的源源文文件件的的全全部部内内容容包包含含到到本本文文件件中中。C语语言言提提供供#include命命令令来来实实现现“文文件件包包含含”的的 操作。一般形式为:操作。一般形式为:#inc1ude“文件名文件名”
21、或或#include#includefile1.cABBfile2.c 常常用用在在文文件件头头部部的的被被包包含含文文件件称称为为“标标题题文文件件”或或“头头部部文文件件”,常常以以“h”为为后后缀缀(h为为head 的的缩缩写写),如如“stdio.h”文文件件。当当然然不不用用“h”为为后后缀缀,而而用用“c”为为后后缀缀或或者者没没有有后后缀缀也也是是可可以以的的,但用但用“h”作后缀更能表示此文件的性质。作后缀更能表示此文件的性质。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四
22、川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1212页页说明:说明:1)一个一个#include命令只能指定一个被包含文件,如果要包含命令只能指定一个被包含文件,如果要包含n个文个文件,要用件,要用n个个#include命令。命令。2)如果如果file1.c文件包含文件包含file2.h文件,而文件文件,而文件file2.h要用到要用到file3.h的内的内容,则可在容,则可在file1.c中用两个中用两个#include命令分别包含文件命令分别包含文件file2.h和和file3.h,而且而且file3.h应出现在应出现在file2.h之前之前。即:
23、即:#include“file3.h”#include“file2.h”这样这样file1.c和和file2.h都可以用到都可以用到file3.h的内容,的内容,并且并且file2的源文件的源文件中不用再声明中不用再声明#include“file3.h”。3)在一个被包含文件中又可以包含另一个被包含文件,即文件包含在一个被包含文件中又可以包含另一个被包含文件,即文件包含可以嵌套。可以嵌套。4)在在#include命令中,文件名要用双引号或尖括号括起来。命令中,文件名要用双引号或尖括号括起来。双引号双引号表示编译系统先在用户当前目录中寻找要包含的文件,若没有,表示编译系统先在用户当前目录中寻找要
24、包含的文件,若没有,则在则在C库函数头文件所在的目录中寻找(库函数头文件所在的目录中寻找(尖括号尖括号的含义)。的含义)。5)被包含文件被包含文件(file2.h)与其所在的文件与其所在的文件(file1.c)在预处理之后已在预处理之后已成为同一个文件成为同一个文件。因此在。因此在 file2.h中若有全局静态变量,它在中若有全局静态变量,它在file1.c中也同样有效,不必用中也同样有效,不必用extern声明。声明。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四
25、川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1313页页9.3 条件编译(自学)一般情况下,源程序中的所有行都要参加编译。但是,一般情况下,源程序中的所有行都要参加编译。但是,有时希望有时希望对源程序中某些内容只在满足一定条件时才进行编对源程序中某些内容只在满足一定条件时才进行编译译,这就是,这就是“条件编译条件编译”。条件编译的形式有:条件编译的形式有:(1)#ifdef 标识符标识符 程序段程序段1#else 程序段程序段2#endif 它的作用是当所指定的标识符已经被它的作用是当所指定的标识符已经被#define命令定义命令定义过,则在程序编译阶段只编译过,则在程序编译
26、阶段只编译程序段程序段1,否则编译,否则编译程序段程序段2。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1414页页 其中其中#else部分可以没有,即部分可以没有,即#ifdef 标识符标识符 程序段程序段1#endif 这里的这里的“程序段程序段”可以是语句组,也可以是命令行。可以是语句组,也可以是命令行。这种条件编译对于提高这种条件编译对于提高C源程序的源程序的通用性通用性是很有好处的。是很有好处
27、的。例如:例如:如果一个如果一个C源程序要在不同的计算机系统上运行,而源程序要在不同的计算机系统上运行,而不同的计算机系统又存在一定的差异(例如,有的机器以不同的计算机系统又存在一定的差异(例如,有的机器以16位来存放一个整数,而有的则以位来存放一个整数,而有的则以32位存放一个整数),这样位存放一个整数),这样往往需要对源程序作必要的修改,这就降低了程序的通用性,往往需要对源程序作必要的修改,这就降低了程序的通用性,可以用以下的条件编译来处理。可以用以下的条件编译来处理。#ifdef COMPUTER_A#define INTEGER_SIZE 16#else#define INTEGER_
28、SIZE 32#endif LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1515页页 如果如果COMPUTER_A在前面已被定义过,则编译下面的在前面已被定义过,则编译下面的命令行:命令行:#define INTEGER_SIZE 16否则,编译下面的命令行否则,编译下面的命令行:#define INTEGER_SIZE 32如果在这组条件编译命令之前曾出现以下命令行:如果在这组条件编译命令之前曾出现以
29、下命令行:#define COMPUTER_A 0或将或将COMPUTER_A定义为任何字符串,甚至是定义为任何字符串,甚至是:#define COMPUTER_A则预处理后程序中的则预处理后程序中的INTEGER_SIZE都用都用16代替,否则都代替,否则都用用32代替。代替。这样,源程序可以不必作任何修改就可以用于不同类型这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。的计算机系统。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学
30、系 C C语言程序设计语言程序设计 第第 1616页页又如:又如:在程序调试时,常常希望输出一些所需的信息,而在程序调试时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段。下的条件编译段。#ifdef DEBUG printf(“x=%d,y=%d,z=%dn”,x,y,z);#endif如果在它的前面有以下命令行:如果在它的前面有以下命令行:#define DEBUG 则在程序运行时输出则在程序运行时输出x,y,z 的值,以便调试时分析。调的值,以便调试时分析。调试完成后只需将命令行试完成后只需
31、将命令行#define DEBUG删去即可。删去即可。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1717页页 (2)#ifndef 标识符标识符 程序段程序段1#else 程序段程序段2#endif 它的作用是当所指定的标识符它的作用是当所指定的标识符未未被被#define命令定义过,命令定义过,则在程序编译阶段只编译则在程序编译阶段只编译程序段程序段1,否则编译,否则编译程序段程序段2。这。这种形式
32、与第一种形式相似,只是在作用上相反。种形式与第一种形式相似,只是在作用上相反。以上两种形式用法差不多,根据需要任选一种,视方以上两种形式用法差不多,根据需要任选一种,视方便而定。便而定。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1818页页 (3)#if 表达式表达式 程序段程序段1#else 程序段程序段2#endif 它的作用是:当指定的它的作用是:当指定的表达式表达式值为真(非零)时就编值为真(
33、非零)时就编译译程序段程序段1,否则编译,否则编译程序段程序段2。不用条件编译命令而直接用不用条件编译命令而直接用if 语句也能达到要求,但语句也能达到要求,但用条件编译命令有如下好处:用条件编译命令有如下好处:可以减少被编译的语句,从可以减少被编译的语句,从而减少目标程序的长度,减少运行时间而减少目标程序的长度,减少运行时间。当使用条件编译。当使用条件编译的程序段比较多时,目标程序的长度可以大大减少。的程序段比较多时,目标程序的长度可以大大减少。OK,本章介绍的预编译功能是,本章介绍的预编译功能是C语言所特有的,有利语言所特有的,有利于提高程序的于提高程序的通用性通用性、可移植性可移植性,使程序变得更加灵活。,使程序变得更加灵活。LanJ copyright LanJ copyright Monday,January 16,2023Monday,January 16,2023 四川理工学院计算机科学系四川理工学院计算机科学系 C C语言程序设计语言程序设计 第第 1919页页第九章 编译预处理9.1 宏定义宏定义 9.2“文件包含文件包含”处理处理 9.3 条件编译条件编译
限制150内