Loading

条件编译

条件编译

问题来源

学习高一凡数据结构与算法树和二叉树这一章节时,遇到如下代码不明白其用法,查阅得知是条件编译的知识,特做此笔记。

#if CHAR 
  typedef char TElemType; 
  TElemType Nil='#'; 
  #define form "%c" 
#else 
  typedef int TElemType; 
  TElemType Nil=0; 
  #define form "%d" 
#endif

引言

假如现在要开发一个C语言程序,让它输出红色的文字,并且要求跨平台,在Windows和Linux下都能运行,该怎么办呢?

这个程序的难点在于,不同平台下控制文字颜色的代码不一样,我们必须要能够识别出不同的平台。Windows有专有的宏_WIN32,Linux有专有的宏_linux_,先看下面的代码:

#include <stdio.h>
int main(){
    if(_WIN32){
        system("color 0c");
        printf("http://c.biancheng.net\n");
    }
    else if(__linux__){
        printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");
    }
    else{
        printf("http://c.biancheng.net\n");
    }

    return 0;
}

但是上面的这段代码是错误的,在Windows下提示_linux_是未定义的标识符,在Linux下提示_WIN32是未定义的标识符。对上面的代码进行改进:

#include <stdio.h>
int main(){
    #if _WIN32
        system("color 0c");
        printf("http://c.biancheng.net\n");
    #elif __linux__
        printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");
    #else
        printf("http://c.biancheng.net\n");
    #endif

    return 0;
}

#if、#elif、#else和#endif都是预处理命令,整段代码的意思是:如果宏_WIN32的值为真,就保留4、5行代码,删除第7、9行代码;如果宏_linux_的值为真,就保留第7行代码;如果所有的宏都为假,就保留第9行代码。

上面的这些操作都是在预处理阶段完成的,多余的代码以及所有的宏都不会参与编译,不仅保证了代码的正确性,还减少了编译后文件的体积。

这种能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能。

三种不同的命令

#if命令

#if命令的完整格式为:

#if 整型常量表达式1
    程序段1
#elif 整型常量表达式2
    程序段2
#elif 整型常量表达式3
    程序段3
#else
    程序段4
#endif

上面这段代码的意思是:如果整型常量表达式1为真(非0),就对程序段1进行编译,否则就计算整型常量表达式2,结果为真的话就对程序段2进行编译,为假的话就继续往下匹配,直到遇到值为真的表达式,或者遇到#else。这一点和ifelse非常类似。

需要注意的是,#if命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;而if后面的表达式没有限制,只要符号语法就行。这是#if和if的一个重要区别。

#if和#else也可以省略,如下所示:

#include <stdio.h>
int main(){
    #if _WIN32
        printf("This is Windows!\n");
    #else
        printf("Unknown platform!\n");
    #endif
   
    #if __linux__
        printf("This is Linux!\n");
    #endif

    return 0;
}

#ifdef命令

#ifdef命令的格式为:

#ifdef 宏名
    程序段1
#else
    程序段2
#endif

上面这段代码的意思是:如果当前的宏已被定义过,则对程序段1进行编译,否则对程序段2进行编译。

也可以省略#else:

#ifdef 宏名
    程序段
#endif

VS/VC有两种编译模式,Debug和Release。在学习过程汇总,我们通常使用Debug模式,这样便于程序的调试;而最终发不的程序,要使用Release模式,这样编译器会进行很多优化,提高程序运行效率,删除荣誉信息。

为了能够清楚地看到当前程序的编译模式,可以在程序中增加提示,看下面的代码:

#include <stdio.h>
#include <stdlib.h>
int main(){
    #ifdef _DEBUG
        printf("正在使用 Debug 模式编译程序...\n");
    #else
        printf("正在使用 Release 模式编译程序...\n");
    #endif

    system("pause");
    return 0;
}

当以Debug模式编译程序时,宏_DEBUG会被定义,预处理器会保留第5行代码,删除第7行代码。反之会删除第5行,保留第7行。

#ifndef命令

#ifndef命令的格式为:

#ifndef 宏名
    程序段1
#else
    程序段2
#endif

与#ifdef相比,仅仅是将#ifdef改为了#ifndef。上面这段代码的意思是:如果当前的宏未被定义,则对程序段1进行编译,否则对程序段2进行编译,这与#ifdef的功能正好相反。

三者的区别

最后需要注意的是,#if后面跟的是整型常量表达式,而#ifdef和#ifndef后面跟的只能是一个宏名,不能是其他的。

例如,下面的形式只能用#if:

#include <stdio.h>
#define NUM 10
int main(){
    #if NUM == 10 || NUM == 20
        printf("NUM: %d\n", NUM);
    #else
        printf("NUM Error\n");
    #endif
    return 0;
}

再如,两个宏都存在时编译代码A,否则编译代码B:

#include <stdio.h>
#define NUM1 10
#define NUM2 20
int main(){
    #if (defined NUM1 && defined NUM2)
        //代码A
        printf("NUM1: %d, NUM2: %d\n", NUM1, NUM2);
    #else
        //代码B
        printf("Error\n");
    #endif
    return 0;
}
posted @ 2021-12-14 09:15  小x蛋x壳  阅读(65)  评论(0编辑  收藏  举报