C语言的预处理器提供了条件编译能力,它使得同一程序在不同的编译条件下能够产生不同的目标代码文件。一般是用条件编译帮助解决程序的可移植性问题,这里把它作为方便编程和调试的手段。在程序中使用这一手段不仅可以对程序进行调试,也可以作为优化比较的手段。关于这一点,将举例进行说明。
1.#ifdef和#endif语句
#ifdef、#endif和#else语句可以作为条件编译控制语句,一般的使用方式是:
#ifdef identifier statements1 #else statements2 #endif
其作用是:如果identifier已定义过,那么statements1参加编译;否则staterments2参加编译。statements1,statements2都可以包含任意语句。上列条件编译中的#else部分可以缺省,于是得:
#ifdef identifier statements #endif
如果identifier定义过,那么statements参加编译,否则不参加编译。
假设有两种型号的计算机WJ1和WJ2,它们有不同的机器字长,若对WJ1使用下列符号常数定义
#define INT SIZE 16
而对WJ2使用下列符号常数定义:
#define INT SIZE 32
如果编写的程序比较大,它包含了很多这种性质的语句,那么随着机器的变换,修改程序的工作量就相当大。使用条件编译提供的能力,则可以缓和这种问题。例如下列语句:
#ifdef WJ1_1 #define INT_SIZE 16 // 第2 句 #else #define INT_SIZE 32 // 第4 句 #endif
其语句含义为,如果WJ1_1在前面已经定义过,第2句将起作用;否则第4句起作用。为了定义符号WJ1_1,最简单的一种方法是使用如下符号常数定义语句:
#define WJ1_1 1
更简单一些使用
#define WJ1_1
于是在源程序中的所有
#ifdef WJ1_1
语句都得到“真”值。
在调试程序时,常常需要插入一些打印语句以显示程序运行轨迹以及产生的中间结果,但是一旦调试结束,这些打印语句就不再需要了。为此可以使用条件编译语句,其形式是:
#ifdef DEBUG statements for debugging #endif
于是,当DEBUG已被定义时,调试语句(statements for debugging)就参加编译,否则不参加。例如有一段程序,它对整型数组data[SIZE]进行处理。在调试这段程序时,希望在若干位置上都将数组各元素的值显示在终端上。这可以事先在程序开始的某处,为了方便,也可以在包含语句后面预先使用语句
#define DEBUG
定义DEBUG,在需要显示数据的地方插入
#ifdef DEBUG printf ( \"date elements :n\" ); for ( i=0 ; i<SIZE ; i++ ) printf ( \"date[%d] = %d n\" , i , data[i] ); #endif
程序段,即可实现输出。当不需要这个输出时,使用“//”将定义注释,即用
// #define DEBUG
的形式使插入的程序段不再参加编译。为了进一步说明它的用法,下面给出一个完整的例子。
【例13.1】使用条件编译调试程序的例子。
#include <stdio.h> #define DEBUG // 定义DEBUG ,使调试信息参加编译 const int SIZE = 2 ; int main (void ) { int data[SIZE]={5 ,8} ,i=0 ; // 插入条件编译的调试信息 #ifdef DEBUG printf ( \"date elements :n\" ); for ( i=0 ; i<SIZE ; i++ ) printf ( \"date[%d] = %d n\" , i , data[i] ); #endif // 程序正常语句 data[1]=data[0]*data[1] ; printf (\"data[0]*data[1] = %dn\" ,data[1] ); return 0 ; }
程序运行结果如下:
date elements : // 调试信息 date[0] = 5 // 调试信息 date[1] = 8 // 调试信息 data[0]*data[1] = 40 // 程序输出结果
如果将第2行的定义用“//”号注释掉,对程序进行再编译时,程序中所有调试语句都不再参加编译,由此产生的目标程序也就会短一些,输出也只有最后一行。
2.#ifndef和#endif语句
另一对条件编译控制语句是:
#ifndef identifier statements #endif
其作用和前面的刚好相反,如果identifier没有定义过,那么statements参加编译,反之不参加。在多文件编程中,如果两个文件重复定义一个头文件,就会发生错误。在第23章的find.h文件中,为了避免重复定义,使用如下方式:
//find.h 文件 #ifndef _H_C6_H // 如果没有定义c6.h #define _H_C6_H // 下面定义c6.h #include <stdio.h> extern const double DIV2 ; // 在头文件中声明为外部常量 double max (double ,double ); double mean (double , double ); #endif // 定义结束
3.#if语句
预处理语句#if提供了按条件控制编译的更一般方法。其一般使用方式是:
#if exp statements 1 #else statements 2 #endif
#if语句测试表达式exp的逻辑值是“真”还是“假”。如若为“真”,则statements1参加编译;否则statements2参加编译。同样,statements1和statements2都可以是一组语句,#else部分(包括statements2)也可以缺省。
#if exp statements 1 #endif
这种方式的方便之处是直接修改exp即可。在程序中,exp直接用1或0代替,1参加编译,0不参加编译。下面是以例13.1为例的例子。
#include <stdio.h> const int SIZE = 2 ; int main (void ) { int data[SIZE]={5 ,8} ,i=0 ; //1 参加编译, 0 不参加编译 #if 1 printf ( \"date elements :n\" ); for ( i=0 ; i<SIZE ; i++ ) printf ( \"date[%d] = %d n\" , i , data[i] ); #endif // 程序正常语句 data[1]=data[0]*data[1] ; printf (\"data[0]*data[1] = %dn\" ,data[1] ); return 0 ; }
“#if 1”调试信息参加编译,如将1改为0,即“#if 0”,则取消调试信息。