c语言 条件编译注释,C之条件编译(二十)

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

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

#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... 这句话。编译后看看结果

4d8da990b3f1acfcc5c223dad57a5c79.png

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

6ce988602576c98d7fbddd18d9c39eec.png

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

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

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

b867dec35e5ff5e2fb8d0f70a3abe48b.png

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

8c812ce51467d26309c0371ce9fa4b34.png

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

28b98f4b5ff6909dc2ce05c82533ef7f.png

我们来创建 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 

#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;

}

我们来看看编译结果

400f9e44f94f5c13a20405f605fe0692.png

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

# 1 ""

# 1 ""

# 1 "/usr/include/stdc-predef.h" 1 3 4

# 1 "" 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

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

33a77f0f07be20112d3da56bedf32361.png

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

# 1 ""

# 1 ""

# 1 "/usr/include/stdc-predef.h" 1 3 4

# 1 "" 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 

#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 中分别定义它们为真,所以应该打印出的是高版本的调试版的。我们看看编译结果

7133ac3fe9630d946ea2ac7b748c013a.png

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

923fe75726ea5663e6a5f06724deb6f8.png

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

041b40017b95c56c2c0ae81b4993b79d.png

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值