首页 > C > 阅读:57,774

预编译

< 上一页 宏的二义性 递归定义 下一页 >

C语言源代码里,除了那些被注释了的代码不会被编译进程序里外,还有一种方式来规定代码是否会被编译进程序里,这就是所谓的条件编译,即代码只有符合某种条件下才能编译进程序。因为有的代码只有在某些情况下才具有被执行的可能,在其它条件下这些代码要么无法执行,要么没有必要执行。比如在Windows平台,有的函数只能在Vista以上平台才能被执行,那么在XP及以下的平台里就无法被执行,因此在XP及以下平台就没有必要将这部分代码编译进程序,有时候甚至还无法在这些平台里编译通过。又比如有的代码只能在X86平台才能执行,在X64平台下是无效的,因此也要对这部分代码进行条件编译。

条件编译可以有多种定义形式。这些定义形式详细介绍如下。

13.5.1方式1

#ifdef  标识符

  程序段1

#else

  程序段2

#endif

在上面这种定义形式下,当标识符被定义(通过#define),那么就将程序段1编译进程序,否则,就将程序段2编译进程序。

#define WINVER 6.1

int main(void)

{

#ifdef WINVER

/*1*/       printf(“wenversion defined\n”);

#else

/*2*/       printf(“winversion not defined\n”);

#endif

             return 0; 

}

上面,通过宏定义将WINVER进行了定义,那么语句1而不是语句2会被编译进程序。在对WINVER进行定义的时候,即使后面不跟任何字符串(比如这里的6.1),也会被当作定义了。比如:

#define WINVER

int main(void)

{

#ifdef WINVER

/*1*/       printf(“wenversion defined\n”);

#else

/*2*/       printf(“winversion not defined\n”);

#endif

              return 0; 

}

这样,语句1依然会被编译进程序,而会排除语句2编译进程序。

 

13.5.2 方式2

#ifndef  标识符

  程序段1

#else

  程序段2

#endif

在方式2的定义下,如果标识符没有定义,那么程序段1会被编译进程序,否则程序段2会被编译进程序。

 

#define WINVER 6.1

int main(void)

{

#ifndef WINVER

/*1*/       printf(“wenversion defined\n”);

#else

/*2*/       printf(“winversion not defined\n”);

#endif

             return 0; 

}

 

同样以上面的程序为例子,在方式2的情况下,如果定义了WINVER,那么语句2会被编译进程序。因为前面的宏定义已经将WINVER进行了定义。

13.5.3 方式3

#if 常量表达式

    程序段1

#else 

    程序段2

#endif

在定义方式3中,如果常量表达式的值为真,那么程序段1将会被编译进程序,否则,程序段2会被编译进程序。

 

#define DEBUG 1

 

int main(void)

{

              int a = 5;

              int b = 10;

              int c = a + b;

#if DEBUG

              /*1*/       printf(“c = %d\n”, c);

#endif

              return 0;

}

在定义了DEBUG1的情况下,那么程序将会把语句1编译进程序,将a+b的结果在调试情况下打印输出。而一旦将DEBUG定义为0的情况下,语句1就无法编译进程序了。比如:

       #define DEBUG 0

 

int main(void)

{

      int a = 5;

      int b = 10;

      int c = a + b;

#if DEBUG

              /*1*/       printf(“c = %d\n”, c); //此句不会被编译进程序

#endif

     return 0; 

}

 

也可以写为:

int main(void)

{

      int a = 5;

      int b = 10;

      int c = a + b;

#if 0

              /*1*/       printf(“c = %d\n”, c); //此句不会被编译进程序

 #endif

       return 0; 

}

13.5.4 方式4

#if 表达式1

       语句段1

#elif 表达式2

       语句段2

#else

       语句段3

#endif

 

#define X64

 

int main(void)

{

#if defined(X64)

       printf(“x64 platform specific\n”);

#elif defined(X86)

       printf(“x86 platform specific\n”);

#else

       printf(“common\n”);

#endif

       return 0; 

}

 

其中defined()用来判断一个标识符是否被定义,如果定义返回为真,否则返回假。而加上个!运算符则表示取反。此外,#undef用来取消对一个标识符的定义,比如:

#ifdef A

#undef A

#endif

上面的条件编译语句,就是在A被定义的情况下取消对A的定义。这个和在头文件的写法中的方式正好相反。

 

现在来看在MS的库中,对LARGE_INTEGER这个64位大整数的定义,就使用了条件编译:

 

#if defined(MIDL_PASS)

typedef struct _LARGE_INTEGER {

#else // MIDL_PASS

typedef union _LARGE_INTEGER {

    struct {

        ULONG LowPart;

        LONG HighPart;

    } DUMMYSTRUCTNAME;

    struct {

        ULONG LowPart;

        LONG HighPart;

    } u;

#endif //MIDL_PASS

    LONGLONG QuadPart;

} LARGE_INTEGER;

 

一旦定义了MIDL_PASS,那么LARGE_INTEGER将会被定义为:

typedef struct _LARGE_INTEGER {

    LONGLONG QuadPart;

} LARGE_INTEGER;

否则会被定义为:

typedef union _LARGE_INTEGER {

    struct {

        ULONG LowPart;

        LONG HighPart;

    } DUMMYSTRUCTNAME;

    struct {

        ULONG LowPart;

        LONG HighPart;

    } u;

    LONGLONG QuadPart;

} LARGE_INTEGER;

 

下面是一个实际工程中使用的一个条件编译的例子:

 

#if WINVER >= 0x0501

    //

    //  Try to load the dynamic functions that may be available for our use.

    //

 

    SfLoadDynamicFunctions();

 

    //

    //  Now get the current OS version that we will use to determine what logic

    //  paths to take when this driver is built to run on various OS version.

    //

 

    SfGetCurrentVersion();

#endif

 

#if DBG && WINVER >= 0x0501

 

    //

    //  MULTIVERSION NOTE:

    //

    //  We can only support unload for testing environments if we can enumerate

    //  the outstanding device objects that our driver has.

    //

 

    //

    //  Unload is useful for development purposes. It is not recommended for

    //  production versions

    //

 

    if (NULL != gSfDynamicFunctions.EnumerateDeviceObjectList) {

 

        gSFilterDriverObject->DriverUnload = DriverUnload;

    }

#endif

注意到,条件编译中提到的语句段,并不一定是一条完整的语句,比如头文件定义方式使用的其实也是条件编译。

13.6 头文件定义

头文件定义的常规写法:

//定义一个名字叫做filename.h的头文件


#ifndef _FILENAME_H_

#define _FILENAME_H_

 

#ifdef  __cplusplus

extern "C" {

#endif

int fucn1(void);

int g_iValue=5;

...

...

...

...

 

#ifdef  __cplusplus

}

#endif

#endif   /* _FILENAME_H_ */

 

头文件定义到此结束。其中:

#ifdef  __cplusplus

extern "C" {

#endif

#ifdef  __cplusplus

}

#endif

结合起来,用于在C++工程中,防止对头文件中的C语言函数进行 名字修饰(name mangling),在中间包含的语段也不是一个完整的语句。

头文件按照这种方式定义,可以防止在工程中被多次包含和编译。

< 上一页 宏的二义性 递归定义 下一页 >

周哥教IT,分享编程知识,提高编程技能,程序员的充电站。跟着周哥一起学习,每天都有进步。

通俗易懂,深入浅出,一篇文章只讲一个知识点。

当你决定关注「周哥教IT」,你已然超越了90%的程序员!

IT黄埔-周哥教IT技术交流QQ群:213774841,期待您的加入!

二维码
微信扫描二维码关注