目录
九、预处理命令
ANSI C标准规定可以在C源程序中加入一些“预处理命令”(preprocessor directives),以改进程序设计的环境,提高编程效率。
C提供的预处理功能主要有以下3种:
-
宏定义
-
文件包含
-
条件编译
分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。
9.1宏定义
不带参数的宏定义
用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为
#define 标识符 字符串
这就是我们已经介绍过的定义符号常量,例如:
#define PI 3.2425926
它的作用是在本程序文件中用指定的标识符PI带代替“3.1415926”这个字符串,在编译预处理时,将程序中在该命令以后出现的所有PI都用"3.1415926"代替。这种方法能以一个简单的名字代替一个长的字符串,因此把这个标识符(名字)称为“宏名”,在预编译时将宏名替换成字符串的过程称为“宏展开”。#define是宏定义命令。
使用不带参数的宏定义。
#include<stdio.h>
#define PI 3.1415926
int main()
{
float l,s,r,v;
printf("input radius:");
scanf("%f",&r) ;
l = 2.0*PI*r;
s = PI*r*r;
v = 4.0/3*PI*r*r*r;
printf("l=%10.4f\ns=%10.4f\nv=%10.4f\n",l,s,v);
return 0;
}
说明:
-
宏名一般习惯用大写字母表示,以便与变量名区别,但这并非规定,也可以用小写字母。
-
使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。使用宏定义,可以提高程序的通用性。
-
宏定义是用宏名代替一个字符串,也就是做简单的置换,不作正确性检查。
-
宏定义不是C语言,不必在行末加分号。如果加了分号则会连分号一起进行置换。如:
#define PI 3.1415926; area = PI*r*r; //经过宏展开后,该语句变为 area = 3.1415926;*r*r; //显然出现语法错误
-
#define命令出现在程序中函数的外面,宏名的有效范围为定义命令到本源文件结束。通常#define命令写在文件开头,函数之前,作为文件的一部分,在此文件范围内有效。
-
可以用#undef命令终止宏定义的作用域。
-
在进行宏定义时,可以引用已定义的宏名,可以层层置换。
#include<stdio.h> #define R 3.0 #define PI 3.1415926 #define L 2*R*PI #define S PI*R*R int main() { printf("L=%f\nS=%f\n",L,S); return 0; } //输出: L=18.849556 S=28.274333
-
对程序中双撇号括起来的字符串内的字符,即使宏名相同,也不进行置换。
-
宏定义时专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。
带参数的宏定义
带参数的宏定义不是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为:
#define 宏名(参数表) 字符串
字符串中包含在括号中所指定的参数。例如:
#define S(a,b) a*b
// ... ...
area = S(3,2); //赋值语句展开为 area = 3*2;
使用带参数的宏。
#include<stdio.h>
#define PI 3.1415926
#define S(r) PI*r*r
int main()
{
float area,a;
a = 3.6;
area = S(a); //不能把实参a+b代入,如S(a+b) 宏展开为area=PI*a+b*a+b;与我们想要的结果不一样 可改宏定义为:#define S(r) PI*(r)*(r)
printf("r=%f\narea=%f\n",a,area);
return 0;
}
//输出:
r=3.600000
area=40.715038
说明:
-
对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。
-
在宏定义时,在宏名与带参数的括号之间不应加空格;否则将空格以后的字符都作为替代字符串的一部分。
带参数的宏与函数的区别:
它们之间有一定的类似之处,在调用函数时也是在函数名后的括号内写实参,也要求实参和形参数目相等。但是在带参数的宏定义与函数是不同的。主要有:
-
函数调用时,先求出实参表达式的值,然后代入形参。而使用带参数的宏只是进行简单的字符替换。
-
函数调用时在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。
-
对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换。而宏不存在类型问题,宏名无类型,它的参数也是无类型,只是一个符号代表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。
-
调用函数只可以得到一个返回值,而宏定义可以设法得到几个结果。
#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 int main() { float l,s,r,v; printf("input radius:"); scanf("%f",&r) ; CIRCLE(r,l,s,v); //展开后的 //l=2*3.1415926*r;s=3.1415926*r*r;v=4.0/3.0*3.1415926*r*r*r; printf("r=%.2f\nl=%.2f\ns=%.2f\nv=%.2f\n",r,l,s,v); return 0; } //输出: input radius:3.5 r=3.50 l=21.99 s=38.48 v=179.59
注意,只要已知参数r的值,就可以从宏CIRCLE的展开中得到三个值(l,s,v)。其实,这只不过是字符代替而已,将字符r代替宏定义中的R,l代替L,s代替S,v代替V,而并未在宏展开时求出l、s、v的值。
-
使用宏次数多时,宏展开后源程序边长,因为每展开一次都使程序增长,而函数调用不会使源程序变长。
-
宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。
一般用宏来代表简短的表达式比较合适。有些问题用宏和函数都可以。
使用宏定义
#include<stdio.h> #define MAX(x,y) (x)>(y)?(x):(y) int main() { int a,b,c,d,t; t = MAX(a+b,b+c); //展开后:t = (a+b)>(c+d)? (a+b):(c+d); // ... ... return 0; }
使用函数
#include<stdio.h> int max(int xlint y) //定义函数max { return(x>y?x:y); } int main() { int a,b,c,d,t; t = max(a+b,b+c); //调用函数max // ... ... return 0; }
声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》如有错误,欢迎大家指正!