第九章

28
第第第

description

第九章. 预处理命令. 本章教学要点. 预处理的概念 不带参宏定义 带参宏定义 文件包含. C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/. 主要内容. 9.1 宏定义 9.2 文件包含 9.3 条件编译. 基本概念. 在 C 源程序中加入一些 “ 预处理命令 ” ,可以改善 C 语言的程序设计环境,提高编程效率。 例如,若程序中用# define 命令定义了符号常量 PI。 #define PI 3.14159 - PowerPoint PPT Presentation

Transcript of 第九章

Page 1: 第九章

第九章

Page 2: 第九章

本章教学要点

预处理的概念预处理的概念 不带参宏定义不带参宏定义 带参宏定义带参宏定义 文件包含文件包含

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

Page 3: 第九章

主要内容

9.1 宏定义 9.2 文件包含 9.3 条件编译

Page 4: 第九章

4

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

基本概念 在 C 源程序中加入一些“预处理命令”,可以改善 C语言的程序设计环境,提高编程效率。 例如,若程序中用 #define命令定义了符号常量 PI。 #define PI 3.14159 经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。其基本处理过程 如图:

Page 5: 第九章

5

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

C提供的预处理功能主要有以下三种: ( 1)宏定义   ( 2)文件包含   ( 3)条件编译 这些功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。 例如: #define #include

基本概念

Page 6: 第九章

6

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.1 宏定义

9.1.1 不带参数的宏定义宏名后不带参数的宏定义是用一个指定的标识符来代表一个字符串。 一般形式: #define 标识符 字符串

Page 7: 第九章

7

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.1 宏定义

例: 不带参数的宏定义示例。#include <stdio.h>#define M (x*x+2*x-1)void main(){ int x,y; printf("input a number:"); scanf("%d",&x); y= x*M+2*M+1; printf("y=%d\n",y); }

程序运行结果:input a number: 2 ↙y=29

在预处理时经宏替换后该语句变为: y=x*(x*x+2*x-1)+2*(x*x+2*x-1)+1 ;

/*即 y=x3+4x2+3x-1*/

Page 8: 第九章

8

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

8.1 宏定义

例如,当进行如下的宏定义: #difine M x*x+2*x-1在宏替换后将得到下述语句: Y=x*x*x+2*x-1+2*x*x+2*x-1+1; /*这相当于 y= x3+2x2+4*x-1; */

Page 9: 第九章

9

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

8.1 宏定义

对于宏定义还要说明以下几点: ( 1)宏定义是用标识符(宏名)来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。 ( 2)宏定义不是声明或语句,在行末不必加分号,如加上分号则连分号也一起置换。

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

Page 10: 第九章

10

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

( 4)宏名如果在源程序中若用引号括起来,则预处理程序不对其作宏替换。 P206例 9.2中的 L

( 5 )宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层替换。 例如: #define PI 3.1415926 #define S PI*y*y /* PI是已定义的宏名 */ 对语句: printf("%f",S); 在宏替换后变为: printf("%f",3.1415926*y*y);

8.1 宏定义

Page 11: 第九章

11

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

( 6)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。 ( 7)可用宏定义表示数据类型,使书写方便。 例如: #define INT int 在程序中可用 INT作变量说明: INT a[10], body[5]; ( 8)如果对输出格式进行宏定义,可以减少程序中重复书写某些字符串的工作量,而对程序结果没有影响。使用宏定义,还可以提高程序的通用性。

Page 12: 第九章

12

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

例: 对输出格式进行宏定义。#define PR printf#define D "%d "#define F "%f\n"#include <stdio.h>void main(){ int a=012, c=-8, e=11; float b=3.8, d=9.7, f=21.08; PR(D F,a,b); PR(D F,c,d); PR(D F,e,f);}

程序运行结果:10 3.800000-8 9.70000011 21.000000

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

Page 13: 第九章

13

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

例如:#define N(x,y) 3*x+y*y /*带参的宏定义 *

/ k=N(7, 9); /*经预处理宏展开后的语句为: k= 3*7+9*9; */

9.1.2 带参数的宏定义 1. 带参宏定义的一般形式 带参宏定义的一般形式为: #define 宏名 (形参表 ) 字符串 调用带参宏定义的一般形式为: 宏名 (实参表 );

Page 14: 第九章

14

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

例: 带参的宏定义示例。#include <stdio.h>#define MAX(a,b) (a>b)?a:bvoid main(){ int x,y,max; printf("input two numbers:"); scanf("%d%d",&x,&y); max=MAX(x,y); printf("max=%d\n",max);}

程序运行结果:input two numbers: 23 8↙

max=23

max=MAX(x,y) ,实参x 、 y 将 替 换 形 参a、 b。

宏展开后该语句为:max=(x>y)?x:y;

Page 15: 第九章

15

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

2. 使用带参宏定义的几点说明 对于带参的宏定义有以下问题需要说明:( 1)带参宏定义中,宏名和形参表之间不能有空格出现。 例如: #define MAX(a,b) (a>b)?a:b 写为: #define MAX (a,b) (a>b)?a:b

( 2)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。

Page 16: 第九章

16

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

例: 带参的宏定义中形参和实参的替换。#include <stdio.h>#define S1(y) (y)*(y)void main(){ int a,sq; printf("input a number: "); scanf("%d",&a); sq=S1(a+1); printf("sq=%d\n",sq); }

程序运行结果:input a number: 6 ↙

sq=49

Page 17: 第九章

17

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

8.1 宏定义

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

P208

( 4 )带参宏定义可用来定义多个语句,在宏调用时,把这些语句又替换到源程序内。 例 9.4带参宏定义可用来定义多个语句。

Page 18: 第九章

18

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

#include <stdio.h> #define PI 3.1415926#define CIRCLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R void main() {float r,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); }

void main() {float r,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); }

Page 19: 第九章

19

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

3. 带参宏定义与函数的区别 (1) 函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。 (2) 函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。 (3) 对函数中的实参和形参类型要求一致。而宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。 (4) 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。 ( 例 9.4 )

Page 20: 第九章

20

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

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

(5) 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不会使源程序变长。 (6) 宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。

Page 21: 第九章

21

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.2 文件包含

所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。 一般形式: #include “文件名” 或: #include <文件名 >

例如:在前面使用输入输出函数和数学函数的头文件时,已多次用到的文件包含命令: #include <stdio.h> #include <math.h>

Page 22: 第九章

22

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.2 文件包含

如图所示

注意: 在编译时并不是分别对两个文件分别进行编译,然后再将它们的目标程序连接的,而是在经过编译预处理后将头文件 file2.c包含到主文件中,得到一个新的源程序,然后对这个文件进行编译,得到一个目标( .obj)文件。被包含的文件成为新的源文件的一部分,而单独生成目标文件。

Page 23: 第九章

23

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.2 文件包含

对文件包含命令还要说明以下几点:( 1)包含命令中的文件名可以用双引号括起来: 使用尖括号(如 <stdio.h>形式)时,系统到存放 C库函数头文件的目录中寻找要包含的文件,这称为标准方式。头文件的包含目录是由用户在设置环境时设置的。

使用双引号(即“ stdio.h”形式)时,系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。

Page 24: 第九章

24

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.2 文件包含

( 2 )一个 include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个 include命令。( 3 )文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。

注意:在程序设计过程中,文件包含不能形成循环嵌套。

Page 25: 第九章

25

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

概念:所谓“条件编译”,是对部分内容指定编译的条件,使其只在满足一定条件才进行编译。

1.条件编译命令的 3种形式:(1) # ifdef 标识符 程序段1 # else 程序段2 # endif

(1) # ifdef 标识符 程序段1 # else 程序段2 # endif

(2) # ifndef 标识符 程序段1 # else 程序段2 # endif

(2) # ifndef 标识符 程序段1 # else 程序段2 # endif

(3) # if 表达式 程序段1  # else 程序段2  # endif

(3) # if 表达式 程序段1  # else 程序段2  # endif

9.3 条件编译

Page 26: 第九章

26

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

9.3 条件编译

#include "stdio.h" #define TED 10 void main () { #ifdef TED printf("Hello Ted\n"); /* 如果定义了 TED,则编译此行代码 *

/ #else printf("Hello anyone\n");/* 如果没用定义 TED,则编译此行代

码 */ #endif #ifndef RALPH printf ("RALPH not defined\n");/* 如果未定义了 RALPH,编译此

代码 */ #endif }

程序运行结果:Hello TedRALPH not defined

2. 条件编译举例

例 : 条件编译 #ifdef的使用。

Page 27: 第九章

27

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/

例 : 条件编译 #if的使用。#include "stdio.h"#define PI 3.14159 #define R 1void main(){ float c,r,s; printf ("input a number: "); scanf("%f",&c); #if R r=PI*c*c; printf("area of round is %f\n",r); #else s=c*c; printf("area of square is %f\n",s);#endif}

程序运行结果:input a number:5↙area of round is 78.539749

Page 28: 第九章

28

23/4/19

C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/