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」,則取消調試信息。