第九章 编译预处理任课老师:台安返回下一页上一页第九章 编译预处理编译预处理 概念
编译,对源程序的说明和执行部分进行词法、语法分析,生成代码,优化等。
编译预处理,在源程序文件中,加入“编译预处理命令”,使编译程序在对源程序进行通常的编译之前,先对这些命令进行预处理,然后将预处理的结果和源程序一起再进行通常的编译处理,以得到目标代码( OBJ文件)。
C提供的编译预处理命令
宏命令( define)
文件包含命令( include)
条件编译命令
这些命令均以 #开头,以区别于语句。
返回下一页上一页第九章 编译预处理第九章 编译预处理
9.1 宏定义
9.1.1 不带参数的宏定义
9.1.2 带参数的宏定义
9.2,文件包含”处理
9.3 条件编译返回下一页上一页第九章 编译预处理
9.1 宏定义
一、不带参数的宏
一般形式,#define 标识符 字符串
如,#define PI 3.1415926
作用:用标识符(称为“宏名”) PI代替字符串,3.1415926”。
在预编译时,将源程序中出现的宏名 PI
替换为字符串,3.1415926”,这一替换过程称为“宏展开”。
#define,宏定义命令返回下一页上一页第九章 编译预处理例 9.1:宏定义
#define PI 3.14159
main( )
{ float l,r,s,v;
scanf(“%f”,&r); /* 输入圆的半径 */
l= r *2.0* PI; /* 圆周长 */
s=r*r*PI; /* 圆面积 */
v=4.0/3.0*r*r*r*PI; /* 球体积 */
printf(“l=%f\ ns=%f\nv=%f\n”,l,s,v);
}
返回下一页上一页第九章 编译预处理关于宏定义的说明:
1、一般宏名用大写字母表示。(变量名一般用小写字母)。
2、使用宏可以提高程序的可读性和可移植性。如上述程序中,
多处需要使用 π值,用宏名既便于修改又意义明确。
3、宏定义是用宏名代替 字符串,宏扩展时仅作简单替换,不检查语法。语法检查在编译时进行。
4、宏定义不是 C语句,后面不能有分号。如果加入分号,则连分号一起替换。
如:
#define PI 3.1415926;
area = PI*r*r;
在宏扩展后成为:
area = 3.1315926; *r*r;
结果,在编译时出现语法错误。
返回下一页上一页第九章 编译预处理关于宏定义的说明:
5、通常把 #define命令放在一个文件的开头,使其在本文件全部有效。
6、宏定义终止命令 #undef结束先前定义的宏名。
#define G 9.8
main( )
{
}
#undef G /* 取消 G的意义 */
f1( )
{

}
返回下一页上一页第九章 编译预处理例 9.2 宏代 换
#define f x+y
main( )
{ int x=4,y=5,z;
if(x ++ < y) z=2*f;
else z=f / 2;
printf(“\n%d”,z);
} 结果请选择:
(A)20 (B) 15 (C) 18 (D) 4
返回下一页上一页第九章 编译预处理二、带参数的宏
一般形式:
#define 宏名(参数表) 字符串
带参数的宏在展开时,不是进行简单的字符串替换,而是进行参数替换。例、
返回下一页上一页第九章 编译预处理
[例 9.3] 带参数的宏
#define PI 3.1415926
#define S(r) PI*r*r
main( )
{ float a,area;
a = 3.6;
area = S(a);
printf("r=%f\narea=%f\n",a,area);
}
返回下一页上一页第九章 编译预处理说明:
1、带参数的宏展开时,用实参字符串替换形参字符串,注意可能发生的错误。
比较好的办法是宏定义的形参加括号。
2、宏定义时,宏名与参数表间不能有空格。例、
#define S□ (r) PI*r*r (□ 表示空格 )
宏定义
#define S(r) PI*r*r
#define S(r) PI*(r)*(r)
语句
area = S(a+b);
area = S(a+b);
展开后
area = PI*a+b*a+b;
area = PI*(a+b)*(a+b)
返回下一页上一页第九章 编译预处理带参数的宏定义与函数的区别:
函数 宏信息传递 实参的值或地址传送给形参。 用实参的字符串替换形参。
处理时刻及内存分配程序运行时处理,分配临时内存单元。
宏展开在预编译时处理,不存在分配内存的问题参数类型 实参和形参类型一致。如不一致,编译器进行类型转换。 字符串替换,不存在参数类型问题。
返回值 可以有一个返回值。 可以有多个返回值。 [例 9.4]
对源程序的影响 无影响 宏展开后使程序加长时间占用 占用程序运行时间。 占用编译时间。
返回下一页上一页第九章 编译预处理
[例 9.4] 返回多个值的宏定义。
#define PI 3.1415926
#define CIRCLE(R,L,S,V) L=2*PI*R; S=PI*R*R;
V=4/3*PI*R*R*R
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);
}
返回下一页上一页第九章 编译预处理
[例 9.5] 输出格式定义为宏
#define PR printf
#define NL "\n"
#define D "%d "
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"
main()
{ int a,b,c,d;
char string[] = "CHINA";
a = 1; b = 2; c = 3; d = 4;
PR(D1,a);
PR(D2,a,b);
PR(D3,a,b,c);
PR(D4,a,b,c,d);
PR(S,string);
}
返回下一页上一页第九章 编译预处理
9.2 文件包含处理
在一个源文件中将另一个源文件的全部内容包含在文件头
形式,#include,文件名”
或:
#include<文件名 >
系统提供的标准函数,在头文件中,如
stdio.h,math.h,string.h等返回下一页上一页第九章 编译预处理文件包含示意图
在 file1.c文件中,有文件包含命令 #include "file2.c",预处理时,
先把 file2.c的内容复制到文件 file1.c,再对 file1.c进行编译。
从理论上说,#include命令可以包含任何类型的文件,只要这些文件的内容被扩展后符合 C语言语法。
#include>file2.c
A
file1.c
B
file2.c
包含
A
B
file1.c
返回下一页上一页第九章 编译预处理
Turbo C 2.0的头文件
一般 #include命令用于包含扩展名为,h的“头文件”,如 stdio.h,string.h,math.h。 在这些文件中,一般定义符号常量、宏,或声明函数原型。
ALLOC.H ASSERT.H BIOS.H CONIO.H CTYPE.H
DIR.H DOS.H ERRNO.H FCNTL.H FLOAT.H
GRAPHICS.H IO.H LIMITS.H MALLOC.H MATH.H
MEM.H PROCESS.H SETJMP.H SHARE.H SIGNAL.H
STDARG.H STDDEF.H STDIO.H STDLIB.H STRING.H
TIME.H VALUES.H
返回下一页上一页第九章 编译预处理程序员也可以把自己定义的符号常量、宏,或函数原型放在头文件中,用 #include命令包含这些头文件
( 1)文件 print_format.h
#define PR printf
#define NL "\n"
#define D "%d "
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"
( 2)文件 file1.c
#include "print_format.h"
main()
{ int a,b,c,d;
char string[] = "CHINA";
a = 1; b = 2; c = 3; d = 4;
PR(D1,a);
PR(D2,a,b);
PR(D3,a,b,c);
PR(D4,a,b,c,d);
PR(S,string);
}
返回下一页上一页第九章 编译预处理说明:
1、一个 include命令只能指定一个被包含文件,如果要包含 n个文件,用 n个
Include命令。
2,#include命令的文件名,可以使用两种括号。
#include "file2.h" 先在引用被包含文件的目录查找 file2.h文件,若没有,再到系统指定的目录查找。
#include <file2.h> 仅在系统指定的目录查找文件 file2.h。
返回下一页上一页第九章 编译预处理说明:
Turbo C系统指定的目录,在 Options|Directores菜单设定。
3、被包含文件与其所在文件,在预处理后,成为一个文件,因此,如果被包含文件定义有全局变量,在其它文件中不必用
extern关键字声明。但一般不在被包含文件中定义变量。
返回下一页上一页第九章 编译预处理
9.3 条件编译
一般情况下,源程序中的所有行均参加编译,
但有时希望部分行在满足一定条件才进行编译,
即对部分内容指定编译的条件,称为“条件编译”。
有以下几种条件编译命令:
一,#ifdef
#ifdef 标识符程序段 1
#else
程序段 2
#endif
#ifdef 标识符程序段 1
#endif
其中,“标识符”用 #define
命令定义返回下一页上一页第九章 编译预处理例、程序调试信息的显示。
#define DEBUG
#ifdef DEBUG
printf("x=%d,y=%d,z=%d\n",x,y,z);
#endif
printf()语句被编译,程序运行时可以显示 x,y,z的值。
在程序调试完成后,不再需要显示 x,y,z的值,则只需要去掉 DEBUG标识符的定义。
直接使用 printf()语句显示调试信息,在程序调试完成后去掉 printf()语句,也可以达到目的。但如果程序中有很多处需要调试观察,增、删语句既麻烦有容易出错。而使用条件编译则相当清晰、方便。
返回下一页上一页第九章 编译预处理二,#ifndef 和 #if
#ifndef 标识符程序段 1
#else
程序段 2
#endif
#ifndef 标识符程序段 1
#endif
#if 表达式程序段 1
#else
程序段 2
#endif
返回下一页上一页第九章 编译预处理
[例 9.8]
#define LETTER 1
main()
{ char str[20] = "C Language",c;
int i;
i = 0;
while((c=str[i]) != '\0')
{ i++;
#if LETTER
if (c>='a' && c<='z')
c = c - 32;
#else
if (c>='A' && c<='Z')
c = c + 32;
#endif
printf("%c",c);
}
}
输入一行字母字符,
根据需要设置条件编译,使之能将字母全改为大写输出,或全该为小写输出。
返回下一页上一页第九章 编译预处理作业与上机:
作业,
9.1,9.2,9.5,9.9,9.10
上机:
1、阅读 Turbo C的 math.h文件,并借助联机帮助系统学习数学计算函数。
2、阅读 Turbo C的 string.h文件,并借助联机帮助系统学习字符串处理函数。
返回下一页上一页第九章 编译预处理本章要求
要求:
1、熟悉宏定义与宏扩展。宏与函数的区别。
2、熟悉文件包含命令 #include的作用及其预处理方法。
3、熟悉条件编译的使用。