首页 > 其他 > 详细

C之条件编译(二十)

时间:2018-04-14 12:27:10      阅读:202      评论:0      收藏:0      [点我收藏+]
        我们在平时的项目中,难免会遇到这样的问题:一个产品需要好几个版本(如低、中、高版本)。那么问题来了,我们需要对这一个产品进行几个版本的人马的同时开发吗?当然是不用啦,企业是讲究效益的,当然是希望一个产品一份代码就搞定啦。在这时我们就可以使用 C 语言中的条件编译啦,它会使同一份代码可以产生不同个版本的产品。下来我们来介绍下条件编译。

        条件编译的行为类似于 C 语言中的 if ... else ... 条件编译是预编译指示命令用于控制是否编译某段代码。我们通过下面这个示例代码进行分析,代码如下

#include <stdio.h>

#define C 1

int main()
{
    const char* s;
    
    #if ( C == 1 )
        s = "This is first printf...\n";
    #else
        s = "This is second printf...\n";
    #endif
    
    printf("%s", s);
    
    return 0;
}

        我们来分析下,如果我们直接编译的话,就会打印 This is first printf... 这句话。编译后看看结果

技术分享图片

        结果如我们分析的那样,下来我们注释掉第3行的宏定义,那么就会打印 This is second printf... 这句话了,这个就不做实验了,大家可以自己做下看看结果是否如我们分析的那样。我们在上面说到 条件编译的行为类似于 C 语言中的看代码的话确实比较像,我们再来单步编译下,看看代码是如何被处理的,为了避免出现不相干的代码 if ... else ... ,我们注释掉头文件和打印语句,结果如下

技术分享图片

        大家可以看到它直接就被替换了。下来我们就讲下如何在宏定义被注释掉的情况下还打印出第一句话,那么这就是我们所谓的条件编译了。

        预编译器根据条件编译指令有选择的删除代码,编译器不知道代码分支的存在。 if ... else ... 语句在运行期进行分支判断,而条件编译指令预编译期进行分支判断。我们可以通过命令行宏定义来指定条件编译,命令如:gcc -Dmacro=value file.c 或 gcc -Dmacro file.c。ps:因为宏定义在 C 语言中还有个起到标识符的作用,因此我们可以直接命令行定义它,使条件为真即可。

        我们将上面的代码中的第3行的宏定义去掉,再将第9行的 #if ( C == 1 ) 变成 #ifden C,再来编译下,看看结果

技术分享图片

        我们再次加上 -Dmacro 选项,编译结果如下

技术分享图片

        我们看到通过在命令行定义宏来达到条件编译的结果。下面我们来讲讲 #include 的本质,它的本质是将已经存在的文件内容嵌入到当前文件中,#include 的间接包含同样会产生嵌入文件内容的操作。我们看看间接包含同一个头文件是否会产生编译错误,结构如下

技术分享图片

        我们来创建 global.h 和 test.h。

global.h 代码

// global.h

int global = 10;

test.h 代码

// test.h

#include "global.h"
const char* NAME = "test.h";
char* hello_world()
{    
    return "Hello world!\n";
}

test.c 代码

#include <stdio.h>
#include "test.h"
#include "global.h"

int main()
{
    const char* s = hello_world();
    int g = global;
    
    printf("%s\n", NAME);
    printf("%d\n", g);
    
    return 0;
}

        我们来看看编译结果

技术分享图片

        它说 global 重复定义了,我们再来仔细看看三个文件,发现只是在 global.h 文件中定义了 global = 10,那么这是怎么回事呢?我们来单步编译下,看看代码是什么样的(同样注释掉头文件和打印语句),由于代码过长,所以就直接上代码了,代码如下

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"

# 1 "test.h" 1


# 1 "global.h" 1


int global = 10;
# 4 "test.h" 2
const char* NAME = "test.h";
char* hello_world()
{
 return "Hello world!\n";
}
# 3 "test.c" 2
# 1 "global.h" 1


int global = 10;
# 4 "test.c" 2

int main()
{
    const char* s = hello_world();
    int g = global;




    return 0;
}

        我们发现在第 14 行定义了 global,在后面包含 global.h 时又重新定义了 global 变量。这样看来编译器报的错误就很正常了,那么我们经常会使用到包含多个头文件,也没见会发生重复定义啊。这时利用条件编译便可以解决头文件重复包含的编译错误,格式如下

#ifndef _HEADER_FILE_H_
#define _HEADER_FILE_H_

// source code

#endif

        我们再次在两个头文件中分别加上条件编译,我们再次编译,结果如下

技术分享图片

        没有报错,而是直接成功执行,我们再来单步编译下,看看代码是怎样的

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"

# 1 "test.h" 1




# 1 "global.h" 1



int global = 10;
# 6 "test.h" 2
const char* NAME = "test.h";
char* hello_world()
{
 return "Hello world!\n";
}
# 3 "test.c" 2


int main()
{
    const char* s = hello_world();
    int g = global;




    return 0;
}

        我们发现 global 变量只定义了一次。这便是我们条件编译的一种应用啦。那么条件编译的意义还不止这些,还有如下几点:a> 条件编译使得我们可以按不同的条件编译出不同的代码段,因而可以产生不同的目标代码;b> #if ... #else .. #endif 被预编译器处理,而 if ... else... 语句被编译器处理,必然被编译进目标代码。在实际的工程中条件编译主要用于以下两种情况:1. 不同的产品线共用一份代码;2. 区分编译产品的调试版和发布版。我们看看下面的示例代码

#include <stdio.h>
#include "product.h"

#if DEBUG
    #define LOG(s) printf("[%s:%d] %s\n", __FILE__, __LINE__, s)
#else
    #define LOG(s) NULL
#endif

#if HIGH
void f()
{
    printf("This is the high level product!\n");
}
#else
void f()
{
}
#endif

int main()
{
    LOG("Enter main() ...");
    
    f();
    
    printf("1. Query Information.\n");
    printf("2. Record Information.\n");
    printf("3. Delete Information.\n");
    
    #if HIGH
    printf("4. High Level Query.\n");
    printf("5. Mannul Service.\n");
    printf("6. Exit.\n");
    #else
    printf("4. Exit.\n");
    #endif
    
    LOG("Exit main() ...");
    
    return 0;
}

 product.h 代码如下

#define DEBUG 1
#define HIGH  1

        我们分析下,如果定义 DEBUG 的话,我们便定义 LOG 日志宏,以便来打印一些信息。如果定义 HIGH 的话,我们便是高版本的了,在 main 函数中除了打印 1  2 3,还将会打印出 4 5 6。如果没定义 HIGH,便打印 4 就完了。因为我么在 product.h 中分别定义它们为真,所以应该打印出的是高版本的调试版的。我们看看编译结果

技术分享图片

        结果如我们分析的那样,我们如果需要一个低版本的发布版,这时只需要在 product.h 中定义 DEBUG 和 HIGH 分别为 0,我们看看结果

技术分享图片

        我们如果需要一个高版本的发布版,这时只需要在 product.h 中定义 DEBUG 为 0 和 HIGH 为 1,结果如下

技术分享图片

        那么我们这时就可以根据我们的需求来编译各种版本的啦。通过对条件编译的学习,总结如下:1、通过编译器命令行能够定义预处理器使用的宏;2、条件编译可以避免重复包含同一个头文件;3、条件编译是在工程开发中可以区别不同产品线的代码;4、条件编译可以定义产品的发布版和调试版


        欢迎大家一起来学习 C 语言,可以加我QQ:243343083

C之条件编译(二十)

原文:http://blog.51cto.com/12810168/2103372

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!