内容正文:
第7章 编译预处理
预处理命令概述
宏定义
文件包含处理
条件编译
7.1 预处理命令概述
C语言程序在编译之前,通常还需要对源文件进行简单的处理,比如文本替换、文件包含、删除部分代码等。在程序编译之前对源文件进行简单加工的过程,就称为预处理。
合理地使用预处理命令会使编写的程序便于阅读、修改移植和调试,也有利于模块化程序设计。
C语言提供的预处理功能主要有3种:宏定义,文件包含和条件编译。
7.2 宏定义
宏定义是用预处理命令#define实现的预处理。
宏定义分为两种形式:带参数的宏定义与不带参数的宏定义。
7.2.1 不带参数的宏定义
一般格式:
#define 标识符 字符串
其中标识符就是宏的名字,简称为宏。
如: #define PI 3.1415926
PI是宏名,字符串3.1415926是替换正文。预处理程序将程序中凡以PI作为标识符出现的地方都用3.1415926替换,这种替换称为宏替换,或者宏扩展。
优点:用一个有意义的标识符代替一个字符串,便于记忆,易于修改,提高程序的可移植性
【例1】计算圆的周长和面积的程序。
#define PI 3.1415926
int main( )
{
float r,c,area;
scanf(“%f”,&r);
c=2*PI*r;
area=PI * r * r;
printf(“C=%.8lf,AREA=%.8lf”,c,area);
return 0;
}
说明:
宏名一般用大写字母,以便与程序中的变量名或函数名区分。
宏替换是在编译之前进行的,由编译预处理程序完成,不占用程序的运行时间。在替换时,只是作简单的替换,不作语法检查。
宏定义不需要使用语句结束符“;” 。
在进行宏定义时,可以使用已定义过的宏名,即宏定义嵌套形式。
一个宏的作用域是从定义的地方开始到本文件结束。也可以用#undef命令终止宏定义的作用域。
一般情况下,宏定义放在函数开头,所有函数之前。也可以把所有定义语句放在单独一个文件里,再把这个文件包含到你的程序文件中。
【例2】宏嵌套定义。
#define PI 3.1415926
#define R 5
#define S PI*R*R
int main( )
{
float c=2*PI*R;
printf(“C=%f,AREA=%f”,c ,S);
return 0;
}
【例3】宏定义。
#define M (y*y+3*y)
int main(){
int s,y;
printf("input a number: ");
scanf("%d",&y);
s=3*M+4*M+5*M;
printf("s=%d
",s);
return 0;
}
运行时输入3,结果输出:?
216
【例4】宏定义。
#define M y*y+3*y
int main(){
int s,y;
printf("input a number: ");
scanf("%d",&y);
s=3*M+4*M+5*M;
printf("s=%d
",s);
return 0;
}
运行时输入3,结果输出:?
135
7.2.2 带参数的宏定义
带参数宏定义的一般形式为:
#define 标识符(参数表) 字符串
字符串中应包含有参数表中所指定的参数。
例如:
#define MIN(x,y) ((x)<(y)?(x):(y))
则语句:c=MIN(3,7);
将被替换为语句:
c=((3)<(7)?(3):(7));
【例5】带参数的宏定义。
#define PI 3.1415926
#define S(r) PI*(r)*(r)
int main( )
{ float c,d,r;
scanf(“%f”,&r);
c=2*PI*r;
d=S(r);
printf(“C=%f,AREA=%f”, c ,d);
return 0;
}
注意:不要写成
#define S (r) P1*(r)*(r)
说明:
在定义宏名时,宏名与带参数的括号之间不应有空格。
在定义宏的命令行中,要注意将字符串及字符串中各个参数用括号括起来。
带参数的宏调用和函数调用非常相似,其主要的区别在于:
带参数的宏替换只是简单的字符串替换,并不进行计算,也不检查参数类型的一致性。
函数调用时,先计算实参表达式的值,再将它的值传给形参,在传递过程中,要检查实参和形参的数据类型是否一致。
【例6】带参数的宏定义。
#define SUM(x,y) x*y
int main( )
{ int d;
d=SUM(3+5,5+5);
printf(“d=%d”, d);
return 0;
}
运行结果:
d=33
分析:进行字符串替换.
d=3+5*5+5
【例7】带参数的宏定义。
#define SUM(x,y) (x)*(y)
int main( )
{ int d;
d=SUM(3+5,5+5);
printf(“d=%d”, d);
return 0;
}
运行结果:d=80
分析:进行字符串替换.
d=(3+5)*(5+5)
注意:带参数的宏中,每一个参数都要用括号括起来,最好在整个表达式外再加括号 #define SUM(x,y) ( (x)*(y) )
15
程序1:
#define SQ(y) y*y
int main()
{ int a=3,sq;
sq=160/SQ(a+1);
printf("sq=%d
",sq);
return 0;
}
程序2:
#define SQ(y) (y)*(y)
int main()
{ int a=3,sq;
sq=160/SQ(a+1);
printf("sq=%d
",sq);
return 0;
}
程序3:
#define SQ(y) ((y)*(y))
int main()
{ int a=3,sq;
sq=160/SQ(a+1);
printf("sq=%d
",sq);
return 0;
}
运行结果:
程序1:57
程序2:160
程序3:10
16
7.3 文件包含处理
文件包含处理是指通过#include命令把另外一
个源文件的内容包含到本文件中。
文件包含的命令格式:
格式1: #include <filename>
格式2: #include “filename”
预处理程序在对源程序文件扫描时,如遇到#include命令,则将指定的filename文件内容替换到源文件中的#include命令行中,并放在#include命令行的位置上。
说明:
在程序设计中常常把一些公用性的符号常量、宏、变量和函数的说明等集中起来组成若干文件,使用时,可以根据需要将相关文件包含进来,这样可以避免在多个文件中输入相同的内容,也为程序的可移植性、可修改性提供了良好的条件。
格式1中使用尖括号< >和格式2中使用双引号“ ”的区别在于:用双引号时,预处理程序首先在当前的目录中检索要包含的文件,若找不到,再按系统指定的标准方式检索其它目录。用尖括号时,不检索当前目录而直接按系统规定的标准方式检索文件目录。
【例8】文件包含
/*file1.c*/
#define PI 3.1415926
#define S(r) P1*(r)*(r)
#define C(r) 2*PI*(r)
#define V(r) 4.0/3.0*P1*(r)*(r)*(r)
/*file2.c*/
#define PI 3.1415926
#define S(r) P1*(r)*(r)
#define C(r) 2*PI*(r)
#define V(r) 4.0/3.0*P1*(r)*(r)*(r)
int main()
{ int r=5;
float s1,c1;
s1=S(r);
c1=C(r);
printf(“%f,%f”,s1,c1);
return 0;
}
/*file2.c*/
#include “file1.c”
int main()
{ int r=5;
float s1,c1;
s1=S(r);
c1=C(r);
printf(“%f,%f”,s1,c1);
return 0;
}
编译前
编译后
/*file3.c*/
#include “file2.c”
int main()
{ int r=10;
float v1;
v1=V(r);
printf(“%f”,v1);
return 0;
}
/*file2.c*/
#include “file1.c”
int main()
{ int r=5;
float s1,c1;
s1=S(r);
c1=C(r);
printf(“%f,%f”,s1,c1);
return 0;
}
7.4 条件编译
整个源程序通常情况都需要参加编译,C语言预处理程序具有条件编译的能力。
使用条件编译命令,可以在满足给定条件时对某些语句进行编译,而在不满足条件时对另外一些语句编译,使同一个源程序在不同的条件下产生不同的目标代码文件。
1、#if形式的一般格式:
#if <表达式>
<程序段1>
[#else
<程序段2>]
#endif
功能:
通过测试表达式的值,若为真,则对程序段1进行编译,否则,编译程序段2。
【例9】
#define X 10
int main()
{
#if(X-10)
printf("X2=%d",X*X);
#else
printf("X3=%d",X*X*X);
#endif
return 0;
}
运行结果:X3=1000
2、 #ifdef形式一般格式:
#ifdef <标识符>
<程序段1>
[#else
<程序段2>]
#endif
功能:
是通过判断#ifdef 后面的<标识符>是否被定义过(一般用#define命令定义),若<标识符>已经定义,则条件为真,编译<程序段1>;否则,条件为假,编译<程序段2>。
3、#ifndef形式一般格式:
#ifndef <标识符>
<程序段1>
[#else
<程序段2>]
#endif
功能:
若#ifndef后面的<标识符>没有被定义,条件为真,编译<程序段1>;否则,条件为假,编译<程序段2>。
本章小结
本章主要介绍编译预处理的三种不同形式:宏定义、文件包含和条件编译。它们是在程序具体编译之前,预先进行处理。使用这些预处理命令可以编写出更易移植、易调试、模块化的程序,从而提高程序的开发效率。
$$