ANSI C标准规定可以在C源程序中加入一些“预处理命令”,来改进程序设计环境,提高编程效率。这些预处理指令都是由ANSI C统一规定的,但是它并不是C语言本身的组成部分,不能直接对其进行编译。
C语言和其它高级编程语言的一个重要区别就是可以使用预处理指令和它的预处理功能。
预处理是指由预处理程序负责完成的在程序编译之前的源代码扫描。预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。当对一个源文件进行编译时,系统会使用预处理程序对源代码中的预处理指令进行扫描,并做相应处理。
C语言的预处理功能主要有三种:
1. 宏定义
在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换, 这称为“宏代换”或“宏展开”。
(1) 不带参数的宏定义
用一个制定的标示符来代表一个字符串,一般形式:
#define 标示符 字符串
例如:
#define A 3.14
这行代码的作用就是用”A”来代表”3.14”,编译程序时,将程序中所有的A都用3.14来代替。
使用示例:
示例1
#include <stdio.h>
#define PI 3.14
void main()
{
float s;
float r = 3.0;
s = r*PI*PI;
printf("半径为%f的圆的面积是%f\n",r,s);
}
上面的代码中,把圆周率PI的值宏定义为3.14,然后在计算圆的面积时直接使用PI,即得出了圆的面积。
示例2
#include <stdio.h>
#define A 3
#define B 4
#define C A+B
void main()
{
int a1 = A+B;
int a2 = C;
// a3 = A+B*A;
int a3 = C*A;
printf("a1的值为%d\na2的值为%d\na3的值为%d\n",a1,a2,a3);
}
以上代码可以看出,宏定义相当于将标示符声明为一个常量,新宏定义一个标示符时,可以使用已定义的标示符。
需要注意的是,宏定义标示符的使用,相当于将其复制过来使用,从a3的值即可看出。
(2) 带参数的宏定义
C语言允许宏带有参数。带参数的宏定义不是进行简单的字符替换,还要进行参数替换。一般形式:
#define SUM(x,y) x+y
上面的宏定义中,定义两个数的和为SUM,两个参数分别为x和y,这两个参数也成为形式参数。在C程序中采用如下的格式使用该宏定义:
s = SUM(2,3);
上面的表达式中,将SUM(2,3)宏展开为2+3;相当于:
s = 2+3;
也就是说宏定义中,将2和3当做宏语句的实际参数代入宏语句后展开,得出计算结果。
使用示例:
#include <stdio.h>
#define PI 3.14
#define S(x) PI*r*r
void main()
{
// area = PI*5*5
float r = 5.0;
float area = S(r);
printf("半径为%f的圆的面积是%f\n",r,area);
}
如上所述,宏定义会将实际参数赋值给形式参数来使用宏定义方法,实现计算圆的面积。
2. 文件包含
文件包含是C预处理程序的另一个重要功能。文件包含命令行的一般形式为: #include“文件名”
或
#include <文件名>
注:””一般用来包含一般源文件,<>一般用来包含系统文件。
#include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准C编译器至少支持八重嵌套包含。
预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。这样就可以在多次包含同一个头文件时,通过给定编译时的条件来达到不同的效果。
所谓文件包含处理是指一个源文件可以讲另外一个源文件的全部内容包含进来,即将另外的文件全部包含在文件中。
说明:
(1) 在一个被包含的文件中还可以包含另外一个被包含的文件,即文件包含是可以嵌套的。
(2) 在#include命令中,文件名可以用爽撇号或者尖括号括起来。两者的区别在于,用<>时,系统到存放C库函数头文件的目录中寻找要包含的文件,这称为标准方式。用””时,系统先到用户当前目录下寻找要包含的文件,若找不到,再按标准方式查找。
(3) 一个#include命令只能指定一个被包含的文件,如果要包含多个文件,要用多个#include指令来完成。
(4) 如果文件a包含文件b,而文件b有又要用到文件c的内容,则可以在文件a中用两个#include命令来分别包含文件b和文件c,而且文件c要在文件b之前被包含,只有这样文件a和文件b才可以都使用文件c中的内容。这是因为预处理包含命令是顺序执行的。
(5) 被包含文件与其所在的文件,在预处理包含命令之后已经成为同一个文件。被包含文件中的所有数据,其所在的文件都可以使用。
3. 条件编译
默认情况下,源程序中所有代码都会参与编译。但是在有些情况下,程序设计有些特别的要求,需要部分源代码参与编译,设置条件,使其在一定的条件下才会参与编译,这就是条件编译。
条件编译的命令有以下几种方式:
(1)#ifdef 标示符
程序段1
#else
程序段2
#endif
作用:若所指定的标示符已经被#define命令定义过,则在程序编译阶段编译程序段1;否则编译程序段2;其中可以没有#else部分。
使用示例:
(2)#ifndef 标示符
程序段1
#else
程序段2
#endif
作用:与第一种作用相反。若所指定的标示符已经未被#define命令定义过,则在程序编译阶段编译程序段1;否则编译程序段2;其中可以没有#else部分。
(3)#if 表达式
程序段1
#else
程序段2
#endif
作用:若所指定的表达式值为真时,则在程序编译阶段编译程序段1;否则编译程序段2;其中可以没有#else部分。
常见预处理指令
指令 | 用途 |
# | 空指令,无任何效果 |
#include | 包含一个源代码文件 |
#define | 定义宏 |
#undef | 取消已定义的宏 |
#if | 如果给定条件为真,则编译下面代码 |
#ifdef | 如果宏已经定义,则编译下面代码 |
#ifndef | 如果宏没有定义,则编译下面代码 |
#elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个#if……#else条件编译块 |
#error | 停止编译并显示错误信息 |