• C和指针 第14章 预处理器 14.3 条件编译


    14.3 条件编译
        在编译一个程序时,如果可以翻译或忽略选定的某条语句或某组语句,常常会很方便。只用于调试程序的语句就是一个明显的例子。它们不应该出现在程序的产品版本中,但是我们可能并不想把这些语句从源代码中物理删除,因为在需要一些维护性修改时,可能需要重新调试这个程序,此时还需要这些语句。
        条件编译(conditional compilation)可以实现这个目的。使用条件编译,可以选择代码的一部分是被正常编译还是完全忽略。用于支持条件编译的基本结构是#if指令和与其匹配的#endif指令。下面显示了它最简单的语法形式:
        #if constant-expression
            statement
        #endif
        其中,constant-expression(常量表达式)由预处理器进行求值。如果它的值是非零值(真),那么statement部分就被正常编译,否则编译器就静默地删除它们。
        所谓常量表达式,就是说它或者是字面值常量,或者是一个由#define定义的符号。如果变量在执行期之前无法获得它们的值,那么它们出现在常量表达式中就是非法的,因为它们的值在编译时是不可预知的。
        例如,将所有的调试代码都以下面这种形式出现:
        #if DEBUG
            printf( "x=%d, y=%d\n", x, y );
        #endif
        这样,无论是想编译还是忽略这个代码,都很容易办到。如果想要编译它,只要使用
        #define DEBUG 1
        这个符号定义就可以了。如果想要忽略它,只要把这个符号定义为0就可以了。无论哪种情况,这段代码都可以保留在源文件中。
        条件编译的另一个用途是在编译时选择不同的代码部分。为了支持这个功能,#if指令还具有可选的#elif和#else子句。完整的语法如下所示:
        #if constant-expression
            statement
        #elif constant-expression
            other-statement ...
        #else 
            other statements 
        #endif
        #elif子句出现的次数可以不限。每个constant-expression(常量表达式)只有当前面所有常量表达式的值都为假时才会被编译。#else子句中的语句只有当前面所有常量表达式的值都为假时才会被编译,在其他情况下都被会忽略。
        K&R C:
        最初的K&R C并没有#elif指令。但是,在这类编译器中,可以使用嵌套的指令来获得相同的效果。
        下面这个例子取自一个以几个不同版本进行销售的程序。每个版本都有一组不同的选项特性。编写这个代码的困难在于如何让它产生不同的版本。必须避免为每个版本编写一组不同的源文件,因为这个代价太大了!由于各组源文件的绝大多数代码都是一样的,维护这个程序将成为一个噩梦。幸运的是,条件编译可以解决这个问题。
        if( feature_selected == FEATURE1 )
        #if    FEATURE1_ENABLED_FULLY
            feature1_function( arguments );
        #elif FEATURE1_ENABLED_PARTIALLY
            feature1_partial_function( arguments );
        #else
            printf( "To use this feature, send $39.95;"
                    " allow ten weeks for delivery.\n" );
        #endif

    /*
    ** 条件编译。 
    */
    #include <stdio.h>
    #include <stdlib.h>

    int main( void ){
        #if !0 
            printf( "!0 is true!\n" );
        #endif
        #if 0
            printf( "0 is false!\n" );
        #endif
        int x, y;
        
        x = 1;
        y = 2;
        #undef DEBUG
        #define DEBUG 0
        #if DEBUG
            printf( "DEBUG = %d, x = %d, y = %d\n", DEBUG, x, y ); 
        #endif
        #undef DEBUG
        #define DEBUG 1
        #if DEBUG
            printf( "DEBUG = %d, x = %d, y = %d\n", DEBUG, x, y ); 
        #endif
        #undef DEBUG
        #define DEBUG 5
        #if DEBUG
            printf( "DEBUG = %d, x = %d, y = %d\n", DEBUG, x, y ); 
        #endif
        #undef DEBUG
        #define DEBUG 6
        #if DEBUG
            printf( "DEBUG = %d, x = %d, y = %d\n", DEBUG, x, y ); 
        #endif
        #undef ch
        #define ch 'A'
        #if ch == 'B'
            printf( "ch == 'B'.\n" );
        #elif ch == 'C'
            printf( "ch == 'C'.\n" );
        #elif ch == 'A'
            printf( "ch == 'A'.\n" );
        #else
            printf( "I don't know what is ch equal to?\n" );
        #endif
        /*
        ** you can regard it as a reference. 
        if( feature_selected == FEATURE1 )
        #if    FEATURE1_ENABLED_FULLY
            feature1_function( arguments );
        #elif FEATURE1_ENABLED_PARTIALLY
            feature1_partial_function( arguments );
        #else
            printf( "To use this feature, send $39.95;"
                    " allow ten weeks for delivery.\n" );
        #endif
        */

        return EXIT_SUCCESS;
    }
    /* 输出:

    */ 

        14.3.1 是否被定义
        测试一个符号是否已被定义也是可能的。在条件编译中完成这个任务往往更为方便,因为如果程序并不需要控制编译的符号所控制的特性,就不需要定义符号。这个测试可以通过下列任何一种方式进行:
        #if defined( symbol )
        #ifdef symbol
        
        #if !defined( symbol )
        #ifndef symbol
        每对定义的两条语句是等价的,但#if形式功能更强。因为常量表达式可能包含额外的条件,如下所示:
        #if X > 0 || defined( ABC ) && defined( BCD )
        K&R C:
        有些K&R C编译器可能并未包含所有这些功能,这取决于它们的年代如何久远。

        14.3.1 是否被定义
        测试一个符号是否已被定义也是可能的。在条件编译中完成这个任务往往更为方便,因为如果程序并不需要控制编译的符号所控
    制的特性,就不需要定义符号。这个测试可以通过下列任何一种方式进行:
        #if defined( symbol )
        #ifdef symbol
        
        #if !defined( symbol )
        #ifndef symbol
        每对定义的两条语句是等价的,但#if形式功能更强。因为常量表达式可能包含额外的条件,如下所示:
        #if X > 0 || defined( ABC ) && defined( BCD )
        K&R C:
        有些K&R C编译器可能并未包含所有这些功能,这取决于它们的年代如何久远。

    /*
    ** 是否被定义。 
    */
    #include <stdio.h>
    #include <stdlib.h>

    int main( void ){
        #undef symbol
        #define symbol 1 
        #if defined( symbol )
            printf( "symbol = %d\n", symbol );
        #endif
        #ifdef symbol
            printf( "symbol = %d\n", symbol );
        #endif
        #undef symbol
        #if !defined( symbol )
            printf( "symbol doesn't be defined.\n" );
        #endif
        #ifndef symbol
            printf( "symbol doesn't be defined.\n" );
        #endif
        #undef X
        #define X 0
        #undef ABC 
        #define ABC 2
        #undef BCD
        #define BCD 0 
        /* the function of #if is stronger than #ifdef */
        #if X > 0 || defined( ABC ) && defined( BCD )
            printf( "X = %d, ABC = %d, BCD = %d\n", X, ABC, BCD );
        #endif
            
        return EXIT_SUCCESS;
    }
    /* 输出:

    */ 

        14.3.2 嵌套指令
        前面提高的这些指令可以嵌套于另一个指令内部,如下面的代码段所示:
        #if defined( OS_UNIX )
            #ifdef OPTION1
                unix_version_of_option1();
            #endif
            #ifdef OPTION2
                unix_version_of_option2();
            #endif
        #elif defined( OS_MSDOS )
            #ifdef OPTION2
                msdos_version_of_option2();
            #endif
        #endif
        在这个例子中,操作系统的选择将决定不同的选项可以使用那些方案。这个例子同时说明了预处理指令可以在它们前面添加空白,形成缩进,从而提高可读性。
        为了帮助大家记住复杂的嵌套指令,可以为每个#endif加上一个注释标签,标签的内容就是#if(或#ifdef)后面的那个表达式。当#if(或#ifdef)和#endif之间的代码块非常长时,这种做法尤为有用。例如:
        #ifdef OPTION1
            lengthy code for option1;
        #else
            lengthy code for alternative;
        #endif /* OPTION1 */
        有些编译器允许一个符号出现于#endif指令中,它的作用和上面这种标签类似。不过这个符号对实际代码不会产生任何作用。标准并没有提及这种做法是否合法,所以更安全的做法还是使用注释。 

    /*
    ** 嵌套指令。 
    */
    #include <stdio.h>
    #include <stdlib.h>

    int main( void ){
        #if defined( OS_UNIX )
            #ifdef OPTION1
                unix_version_of_option1();
            #endif
            #ifdef OPTION2
                unix_version_of_option2();
            #endif
        #elif defined( OS_MSDOS )
            #ifdef OPTION2
                msdos_version_of_option2();
            #endif
        #endif
        #ifdef OPTION1
            /* lengthy code for option1; */
        #else
            /* lengthy code for alternative; */
        #endif /* OPTION1 */
        
        return EXIT_SUCCESS;
    }
    /* 输出:

    */ 

  • 相关阅读:
    【mmWave】一、IWR6843ISK-ODS毫米波雷达【固件烧写和上手使用】流程
    luogu p3047题解
    设置centos系统语言设置为中文
    外星人:可惜,地球人的AI科技树长歪了!
    HTTP 错误 500.19 - Internal Server Error 无法访问请求的页面,因为该页的相关配置数据无效——错误代码:0x8007000d
    Pytorch优化器全总结(二)Adadelta、RMSprop、Adam、Adamax、AdamW、NAdam、SparseAdam(重置版)
    【计算机网络笔记】DNS报文格式
    【信息检索与数据挖掘期末笔记】(四)概率检索模型
    电容笔有必要买苹果原装的吗?ipad第三方电容笔了解下
    计算机网络第三章习题
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/125610766