服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言 深入讲解条件编译的用处

C语言 深入讲解条件编译的用处

2022-11-11 14:41清风自在 流水潺潺 C/C++

C语言提供了条件编译的语法,就是在编译源码的时候,可以选择性地编译指定的代码。例如我们开发一个兼容windows系统和linux系统运行的项目,那么,一些与操作系统密切相关的代码,就需要进行选择性编译

一、基本概念

  • 条件编译的行为类似于 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;
}

下面为输出结果:

C语言 深入讲解条件编译的用处

可以输入gcc -E Test.c -o file.i 命令,看看预编译阶段发生了什么,下面是部分输出结果:

# 2 "Test.c" 2

int main()
{
  const char* s;

      s = "This is first printf...\n";

  printf("%s", s);

  return 0;
}

可以看到宏定义和条件编译都没有了,由相应内容取而代之。

 

二、条件编译的本质

预编译器根据条件编译指令有选择的删除代码

编译器不知道代码分支的存在

if...else... 语句在运行期进行分支判断

条件编译指令在预编译期进行分支判断

可以通过命令行定义宏

  • gcc -Dmacro=value file.c
  • gcc -Dmacro file.c

下面看一个通过命令行定义宏的代码:

#include <stdio.h>
int main()
{
  const char* s;

  #ifdef C
      s = "This is first printf...\n";
  #else
      s = "This is second printf...\n";
  #endif

  printf("%s", s);
  
  return 0;
}

终端输入gcc -DC Test.c,输出结果如下:

C语言 深入讲解条件编译的用处

 

三、#include 的本质

  • #include 的本质是将已经存在的文件内容嵌入到当前文件中
  • #include 的间接包含同样会产生嵌入文件内容的操作

这就出现一个问题,间接包含同一个头文件是否会产生编译错误?

C语言 深入讲解条件编译的用处

下面就来通过一段代码深入探究:

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 重定义:

C语言 深入讲解条件编译的用处

为什么 global 会重定义呢?下面开始单步编译,输入gcc -E test.c -o test.i,输出部分结果如下:

# 2 "test.c" 2
# 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;

  printf("%s\n", NAME);
  printf("%d\n", g);

  return 0;
}

这样就很明显了,程序先将 test.h 里面的东西复制进 test.c,由于 test.h 里面有一个 include "global.h",就把int global = 10; 复制过来,然后复制

const char* NAME = "test.h";

char* hello_world()

{undefined

return "hello world!\n";

}

在然后由于test.c 里面又定义一个#include "global.h",又把int global = 10; 复制过来,造成了重复定义。

条件编译可以解决头文件重复包含的编译错误

#ifndef _HEADER_FILE_H_
#define _HEADER_FILE_H_
//source code
#endif

如果没有定义 header_file.h,则定义,且执行里面的代码;否则,如果定义了,里面的代码就不会执行。

所以上述代码中可以这么改:

global.h:

// global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
int global = 10;

#endif

test.h:

// test.h
#ifndef _TEST_H_
#define _TEST_H_
#include "global.h"
const char* NAME = "test.h";
char* hello_world()
{
  return "hello world!\n";
}
#endif

这样编译就能通过了

C语言 深入讲解条件编译的用处

 

四、条件编译的意义

条件编译使得我们可以按不同的条件编译不同的代码段,因而可以产生不同的目标代码

#if...#else...#endif 被预编译器处理,而 if...else... 语句被编译器处理,必然被编译进目标代码

实际工程中条件编译主要用于以下两种情况:

  • 不同的产品线共用一份代码
  • 区分编译产品的调试版和发布版

下面看一段产品线区分及调试代码:

product.h:

#define DEBUG 1
#define HIGH  1

test.c:

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

宏 DEBUG 是指产品是调试版还是发布版,调试版为 1,发布版为 0, 宏 HIGH指的是产品是高端产品还是低端产品,高端产品为 1,低端产品为 0

如果我们想测试调试版的高端产品,令 DEBUG 为 1,HIGH为 0 即可:

C语言 深入讲解条件编译的用处

同理,我们想测试发布版的低端产品,令 DEBUG 为 0,HIGH为 0 即可:

C语言 深入讲解条件编译的用处

五、小结

  • 通过编译器命令行能够定义预处理器使用的宏
  • 条件编译可以避免重复包含头同一个头文件
  • 条件编译是在I程开发中可以区别不同产品线的代码
  • 条件编译可以定义产品的发布版和调试版

到此这篇关于C语言 深入讲解条件编译的用处的文章就介绍到这了,更多相关C语言 条件编译内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43129713/article/details/123474028

延伸 · 阅读

精彩推荐
  • C/C++适合初学者的C语言数据类型的讲解

    适合初学者的C语言数据类型的讲解

    在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。...

    每天都要学一点哦5722022-11-04
  • C/C++C语言实现歌曲信息管理系统

    C语言实现歌曲信息管理系统

    这篇文章主要为大家详细介绍了C语言实现歌曲信息管理系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    hackbuteer18812021-06-18
  • C/C++一篇文章带你入门C++的异常处理

    一篇文章带你入门C++的异常处理

    C++ 提供了异常机制,让我们能够捕获运行时错误,本文就详细的介绍了C++异常处理入门,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    物随心转9162022-01-05
  • C/C++C++内存管理详细解析

    C++内存管理详细解析

    这篇文章主要给大家分享的是C++内存管理的详细内容学习,下面文章围绕C++内存管理的相关资料展开具体学习内容,需要的朋友可以参考一下,希望对你有所...

    妙妙园9942022-02-28
  • C/C++华为面试题答案找出最大长度子字符串

    华为面试题答案找出最大长度子字符串

    华为面试题:找出最大长度子字符串,打印并且返回长度。 例如 str = "abc123abcd234abcdefgha324adsdawqdasdaseqqwe345abchded",看下面的代码实现吧...

    C语言教程网9302021-01-11
  • C/C++C语言中操作sqlserver数据库案例教程

    C语言中操作sqlserver数据库案例教程

    这篇文章主要介绍了C语言中操作sqlserver数据库案例教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    uestclr11842021-12-01
  • C/C++C语言光标旋转与倒计时功能实现示例详解

    C语言光标旋转与倒计时功能实现示例详解

    这篇文章主要为大家介绍了C语言实现光标旋转与倒计时功能的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪...

    鹿九丸11682022-03-02
  • C/C++C语言修炼之路悟彻数组真妙理 巧用下标破万敌上篇

    C语言修炼之路悟彻数组真妙理 巧用下标破万敌上篇

    在C语言和C++等语言中,数组元素全为指针变量的数组称为指针数组,指针数组中的元素都必须具有相同的存储类型、指向相同数据类型的指针变量。指针数...

    玄澈_8502022-10-09