280 likes | 381 Views
第九章. 预处理命令. 本章教学要点. 预处理的概念 不带参宏定义 带参宏定义 文件包含. C 程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/. 主要内容. 9.1 宏定义 9.2 文件包含 9.3 条件编译. 基本概念. 在 C 源程序中加入一些 “ 预处理命令 ” ,可以改善 C 语言的程序设计环境,提高编程效率。 例如,若程序中用# define 命令定义了符号常量 PI。 #define PI 3.14159
E N D
第九章 预处理命令
本章教学要点 预处理的概念 不带参宏定义 带参宏定义 文件包含 C程序设计教程 ---------- http://www.tup.tsinghua.edu.cn/
主要内容 9.1 宏定义 9.2 文件包含 9.3 条件编译
基本概念 • 在C源程序中加入一些“预处理命令”,可以改善C语言的程序设计环境,提高编程效率。 例如,若程序中用#define命令定义了符号常量PI。 #define PI 3.14159 • 经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。其基本处理过程 如图:
基本概念 C提供的预处理功能主要有以下三种: (1)宏定义 (2)文件包含 (3)条件编译 这些功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。 例如: #define #include
9.1 宏定义 9.1.1 不带参数的宏定义 宏名后不带参数的宏定义是用一个指定的标识符来代表一个字符串。 一般形式: #define 标识符 字符串
9.1 宏定义 程序运行结果: input a number: 2 ↙ y=29 例: 不带参数的宏定义示例。 #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); } 在预处理时经宏替换后该语句变为: y=x*(x*x+2*x-1)+2*(x*x+2*x-1)+1; /*即y=x3+4x2+3x-1*/
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;*/
8.1 宏定义 对于宏定义还要说明以下几点: (1)宏定义是用标识符(宏名)来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。 (2)宏定义不是声明或语句,在行末不必加分号,如加上分号则连分号也一起置换。 (3)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如果要终止其作用域,可使用#undef命令。
8.1 宏定义 (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);
(6)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。(6)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。 (7)可用宏定义表示数据类型,使书写方便。 例如: #define INT int 在程序中可用INT作变量说明: INT a[10], body[5]; (8)如果对输出格式进行宏定义,可以减少程序中重复书写某些字符串的工作量,而对程序结果没有影响。使用宏定义,还可以提高程序的通用性。
例: 对输出格式进行宏定义。 #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.700000 11 21.000000 注意:宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。
9.1.2 带参数的宏定义 1. 带参宏定义的一般形式 带参宏定义的一般形式为: #define 宏名(形参表) 字符串 调用带参宏定义的一般形式为: 宏名(实参表); 例如: #define N(x,y) 3*x+y*y /*带参的宏定义*/ k=N(7,9); /*经预处理宏展开后的语句为:k= 3*7+9*9;*/
程序运行结果: input two numbers: 23 8↙ max=23 例: 带参的宏定义示例。 #include <stdio.h> #define MAX(a,b) (a>b)?a:b void main() { int x,y,max; printf("input two numbers:"); scanf("%d%d",&x,&y); max=MAX(x,y); printf("max=%d\n",max);} max=MAX(x,y) ,实参 x、y将替换形参a、b。 宏展开后该语句为: max=(x>y)?x:y;
2. 使用带参宏定义的几点说明 对于带参的宏定义有以下问题需要说明: (1)带参宏定义中,宏名和形参表之间不能有空格出现。 例如: #define MAX(a,b) (a>b)?a:b 写为:#define MAX (a,b) (a>b)?a:b (2)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
例: 带参的宏定义中形参和实参的替换。 #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
8.1 宏定义 (3)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。 P208 (4)带参宏定义可用来定义多个语句,在宏调用时,把这些语句又替换到源程序内。 例9.4带参宏定义可用来定义多个语句。
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); } • #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); • }
3. 带参宏定义与函数的区别 (1) 函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。 (2) 函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。 (3) 对函数中的实参和形参类型要求一致。而宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。 (4) 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。(例9.4)
(5) 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不会使源程序变长。 (6) 宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。 说明: 如果善于利用宏定义,可以实现程序的简化,如事先将程序中的“输出格式”定义好,以减少在输出语句中每次都要写出具体的输出格式的麻烦。
9.2 文件包含 所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。 一般形式:#include “文件名” 或: #include <文件名> 例如:在前面使用输入输出函数和数学函数的头文件时,已多次用到的文件包含命令: #include <stdio.h> #include <math.h>
9.2 文件包含 如图所示 注意:在编译时并不是分别对两个文件分别进行编译,然后再将它们的目标程序连接的,而是在经过编译预处理后将头文件file2.c包含到主文件中,得到一个新的源程序,然后对这个文件进行编译,得到一个目标(.obj)文件。被包含的文件成为新的源文件的一部分,而单独生成目标文件。
9.2 文件包含 对文件包含命令还要说明以下几点: (1)包含命令中的文件名可以用双引号括起来: 使用尖括号(如<stdio.h>形式)时,系统到存放C库函数头文件的目录中寻找要包含的文件,这称为标准方式。头文件的包含目录是由用户在设置环境时设置的。 使用双引号(即“stdio.h”形式)时,系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。
9.2 文件包含 (2)一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。 (3)文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。 注意:在程序设计过程中,文件包含不能形成循环嵌套。
9.3 条件编译 概念:所谓“条件编译”,是对部分内容指定编译的条件,使其只在满足一定条件才进行编译。 1.条件编译命令的3种形式: (1)#ifdef 标识符 程序段1 #else 程序段2 #endif (2)#ifndef 标识符 程序段1 #else 程序段2 #endif (3) #if 表达式 程序段1 #else 程序段2 #endif
9.3 条件编译 2. 条件编译举例 例: 条件编译#ifdef的使用。 程序运行结果: Hello Ted RALPH not defined #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 }
例: 条件编译#if的使用。 #include "stdio.h" #define PI 3.14159 #define R 1 void 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
THE END THE END THE END