《c程序设计第九章.ppt》由会员分享,可在线阅读,更多相关《c程序设计第九章.ppt(30页珍藏版)》请在三一文库上搜索。
1、,第九章 编译预处理,,本章内容,1、编译预处理,2、宏定义,3、文件包含,4、条件编译,5、本章小结,,编译预处理,编译预处理是指,在对源程序进行编译之前,系统将自动引用预处理程序对源程序中的预处理部分作处理;然后再将处理的结果,和源程序一起进行编译,以得到目标代码。,具体过程如下:,,语言提供了多种预处理功能,如宏定义、文件包含、 条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、 移植和调试,也有利于模块化程序设计。 预处理的命令有以下几个特点: 1预处理命令均以#开头,结尾不加分号; 2预处理命令可以放在程序中任何位置,作用范围从定义处到文件结尾。,编译预处理,,宏定义,宏提供
2、了用一个标识符来表示一个字符串的机制,实际上就是一种替换,有时称为宏替换。在编译预处理时,对程序中所有出现的“宏”,都用宏定义中的字符串去代换。 宏定义由宏定义命令完成,宏代换是由预处理程序自动完成的。,使用宏的优点: (1)可提高源程序的可维护性、可移植性 (2)减少源程序中重复书写字符串的工作量,,宏定义,C语言中的宏分为有参宏和无参宏两种,1、无 参数的宏,define: 宏名的关键字,标识符: 定义的宏名,通常用大写字母取名,以便于与变量区别; 语言符号字符串: 即宏体,可以是常数、表达式、格式串等。例如: #define PI 3.1415926 定义了一个符号常量PI,表示用标识符
3、PI替换3.1415926。在编译预处理时,会将程序代码中所有的PI都用3.1415926替换。,,(1)宏名一般用大写字母表示,便于与变量名区别;一般将字符个数较多的字符串用一个宏名替换,减少程序中多处引用字符串书写错误。 (2)宏定义是用宏名来表示一个字符串,在宏展开时以该字符串取代宏名,这只是一种简单的代换, 预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。 (3)宏定义不是语句,在行末不加分号,如加上分号则连分号也一起置换。,2. 关于不带参数的宏定义的几点说明:,(4)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如 要终止其作用域可使用#
4、undef命令。例如: # define PI 3.14159 main( ) # undef PI /*终止宏定义PI*/ min( ) 表示PI只在main函数中有效,在min中无效。,宏定义,,(5) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。,2. 关于不带参数的宏定义的几点说明:,宏定义,(6) 宏定义可以嵌套,但嵌套的宏定义名要是已经定义的宏名。在宏展开时由预处理程序层层替换。例如: #define R 14.256 #define PI 3.1415926 #define S PI*R*R /* PI、R是已定义的宏名*/ 则语句 printf (“%f“, s)
5、;在宏代换后变为 printf (“%f“,3.1415926*14.256*14.256);,(7)可用宏定义表示数据类型,使书写方便。 例如: #define STU int 在程序中可用STU替换数据类型int。,,宏定义,3、无参宏的应用举例,例 9.1 不带参数的宏的程序 #include #define PI 3.1415926 #define STRING This is a test main( ) float r, s; printf (“ STRING n“); scanf (“%f“, ,宏定义不是语句,所以不能在行尾加分号。否则,宏展开时,会将分号作为宏体中的1个字符。
6、,在宏展开时,预处理程序仅以宏体替换宏名,而不作任何检查,,宏定义,3、无参宏的应用举例,例 9.2在进行宏定义时,可以引用已定义的宏名。 #define PI 3.1415926 #define L PI*r*2 #define AR PI*r*r #define VO AR*r*4/3 main( ) float r; printf(“ Input a radius: “); scanf(“ %f“, ,在进行宏定义时,可以引用已定义的宏名,层层置换,,宏定义,3、无参宏的应用举例,例9.3 #define N 2 #define M N+1 #define NUM 2M+1 main(
7、) int i; for (i=1; i=NUM; i+) printf (“ %d n”, i ); 宏展开后的等价程序如下: main( ) int i; for (i=1; i=6; i+) printf(“ %d n”, i ); ,NUM宏展开: 2*N+1+1 再宏展开: 2*2+1+1,,宏定义,带参宏定义说明: (1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则,编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。 (2)有参宏的展开,只是将实参作为字符串,简单地置换形参字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整个字符串外,均加一对圆括号。
8、,4带参宏定义,(1)带参宏定义格式:#define 宏名(形参表) 宏体 (2)带参宏调用格式:宏名(实参表) (3)宏展开:用宏调用提供的实参字符串,直接置换宏定义命令行中相应形参字符串,非形参字符保持不变,,例9.4 带参数的宏 #define DS(a) 1.0/a main() float r1, r2, r ; r1=5.5; r2=50.8; r=DS(r1)+DS(r2); printf (“R=%fn”, 1.0/r); 运行结果: R=4.962700,形参:a 实参: r1, r2,宏定义,4带参宏定义,,例9.5 带参数的宏 #include #define F(a)
9、a*a /*宏名F ,形参a*/ main( ) int x=3,y=4, b, z; b=x+y; z=F(x+y); printf(“b=%dnz=%dn“,b,z); ,/* 展开为z=x +y*x+y ;*/,宏定义,4带参宏定义,,(1)在函数调用时,是先求出实参表达式的值,再传递给形参,而宏定义只是简单的字符替换; (2)函数调用是在程序运行时处理的,分配存储单元,而宏展开(调用)是在编译预处理时进行的,展开时不分配内存单元,不进行值传递,没有返回值的概念; (3)对函数实参和形参都要定义类型,而宏不存在类型,宏定义时字符串可以是任何类型数据,一律看成字符串,宏名也没类型,只是一个
10、符号表示,展开时代入指定的符号即可。,5.带参的宏和带参函数区别,宏定义,,(4)定义带参数的宏,可以实现一些简单的函数功能。 #define MAX(x,y) (x)(y)?(x) : (y) main( ) int a, b, c, t ; t = MAX(a+b, c+d); /* 展开后为 t = (a+b)(c+d)?(a+b):(c+d)* / 如果第一行写成:#define MAX(x,y) xy ? x : y 则展开后为 t = a+bc+d?a+b:c+d;因为置换展开是用“表达式”对等的置换“形参表”中的参数。,5.带参的宏和带参函数区别,宏定义,,例9.6 带参数的宏的
11、宏体中有() #define SA(i) i*i #define SB(j) (j)*(j) main( ) int a,b,x,y; a=3; b=7; x=SA(a+b)/SA(a+b); /* x=a+b*a+b/a+b*a+b */ y=SB(a+5)/SB(b+2); /* y=(a+5)*(a+5)/(b+2)*(b+2) */ printf(“x=%d,y=%dn”,x,y); 运行结果: x=54,y=63,5.带参的宏和带参函数区别,宏定义,,文件包含,1文件包含的概念文件包含是指,一个源文件可以将另一个源文件的全部内容包含进来。 2文件包含处理命令的格式 include “
12、包含文件名” 或 include 两种格式的区别仅在于: (1)使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。 (2)使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。,,一个大程序,通常分为多个模块,并由多个程序员分别编程。 有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。,文件包含,3文件包含
13、的优点,,4说明 (1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。 (2)常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。 (3)一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。 (4)文件包含可以嵌套,即被包含文件中又包含另一个文件。,文件包含,,file2.c /*显示file2.c内容*/ fun2( ) printf(“file2 includen”); file1.c # incl
14、ude “file2.c” /*包含file2.c文件*/ fun1( ) printf(“file1 includen”); fun2(); expfile.c # include “file1.c” /*包含file1.c文件*/ main( ) printf(“expfile includen”); fun1(); printf(“endn”) ;,文件包含,,条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。 条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。 (1)在不同的系统中,一个i
15、nt 型数据占用的内存字节数可能是不同的。 (2)利用条件编译,还可使同一源程序即适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。,条件编译,,# ifdef 标识符 程序段1 【else 程序段2】 #endif如果标识符(宏定义的标识符)已被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译。 如果没有程序段2,本格式中的#else可以没有, 即可以写为: # ifdef 标识符 程序段 #endif,条件编译,1. 第一种形式,,例9.7 条件编译实例1 #include #define TED 10 main( ) #ifdef TED p
16、rintf(“Hi,Tedn“); #else printf(“Hi,Anyonen“); #endif #ifndef RALPH printf(“RAPLH not definedn“); #endif ,条件编译,,# ifndef 标识符 程序段1 #else 程序段2 #endif 与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译, 否则对程序段2进行编译。这与第一种形式的功能正相反。,条件编译,2. 第二种形式,,# if 常量表达式 程序段1 #else 程序段2 #endif 它的功能是,如常量表达
17、式的值为真(非0),则对程序段1 进行编译,否则对程序段2进行编译。可以使程序在不同条件下,完成不同的功能。,条件编译,3. 第三种形式,,例 9.8 条件编译实例2 #include #define MAX 10 main() #if MAX99 printf(“compile for array greater than 99“); #else printf(“compile for small array“); #endif ,条件编译,,例 9.9 条件编译实例3 #define LETTER 1 main( ) char str20 = “C labguage”, c ; int i
18、 = 0; while ( ( c = stri != 0 ) i + + ; # if LETTER if ( c = a ,运行结果: C LANGUAGE,条件编译,,内容小结,本章主要讲述了编译预处理命令的作用、定义和相关注意事项。在学习的过程要紧密结合本章的以下知识要点: 编译预处理命令以“#”开头的一些命令。主要有下列几种形式。 宏替换 宏替换也称宏定义,这是以“#define”开头的一类命令,它的基本功能是用1个名字来代表1个常数或一段程序代码,在应用中可以有以下两种形式: 不带参数的宏: #define 宏名 宏体 带参数的宏: #define 宏名(参数名1,参数名2,参数名
19、n) 宏体 后一种形式的功能要比前一种强大,在定义中的3部分必须以空格分隔,无论什么样的宏,系统只进行原样替换而不进行计算,因此宏替换无计算功能。 文件包含 文件包含处理是指1个源文件可以将另外1个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。,,1、以下程序的输出结果是 A) 15 B) 100 C) 10 D) 150 #define MIN(x,y) (x)(y)?(x):(y) main( ) int i,j,k; i=10: j=15; k=10*MIN(i,j); printf(%dn“,k); 2、以下程序中的for循环执行的次数是 A)5 B)6 C)8 D)9 #define N 2 #define M N+1 #define NUM (M+1)*M/2 main( ) int i; for (i=1:i=NUM; i+); printf(“%dn“,i); ,课后作业,
链接地址:https://www.31doc.com/p-2102408.html