【C语言更新】条件编译

文/Edward


有时候我们在做项目的时候,一个全新的项目代码并不会全部重新写,这个新项目往往只是某一个主线项目的分支,因此可以借鉴之前的代码,然后对需要修改的地方进行修改。但是,如果这两份代码改动的地方仅仅是某些逻辑判断,或是某些AD电压阈值判断标准。其余两份代码之间的架构完全相同,那么其实新代码基于老代码的改动微乎其微,其代码量相似度可能达到95%以上。那么有没有一种方法可以将这两份代码统一成一份,并且根据不同的选项编译不同的代码。这样的话也便于程序开发以及后续的代码管理。

在C语言里面有一种机制可以决定编译那些代码,这个机制就是预处理中的条件编译。
使用条件编译,你可以选择代码的一部分是被正常编译还是完全忽略。C语言中条件编译的主要形式有三种,接下来我们将会对其一一讨论。

  #if……#endif
第一种最简单的选择编译形式就是判断条件编译。这里面会用到两个预处理命令#if和#endif。其表达形式为:
#if 常量表达式 执行语句#endif
其中的常量表达式是由预处理器进行求值。如果它的值是非零值(真),那么执行语句部分就会参与到代码的编译中来。否则,预处理器就会忽略它们,使它们不参与编译。如图8-3-1中的代码。
图8-3-1 判断类型条件编译

在图8-3-1所示的代码中,我们使用#define定义了两个符号,一个符号是DEBUG1,其常量值为0,又定义了DEBUG2,其常量值为1。在程序执行处,我们可以看到有两个判断类型的条件编译。一旦满足一个条件,就将其相应的printf函数进行编译。最终我们可以看到,只有当DEBUG2这个调试信息可以打印出来,就是由于DEBUG2为1,满足了#if的条件编译,因此其相应的代码会被编译进去。
需要注意一点的是,这里面的#if和之前我们学过的条件判断语句if有着不同的机制,条件判断语句的if是将整个if……else中的代码编译进工程。而#if是预处理命令,因此其产生的影响是文本级别的,即在预处理阶段直接修改.c源文件。我们可以查看其中间文件,如图8-3-2所示。
图8-3-2条件编译的中间文件

如图8-3-2中所生成的中间文件a.out,我们可以看到,只要不满足#if条件的代码,它完完全全会被编译器舍弃。
#if……#endif还有一种扩展的形式,即多重条件判断的条件编译。其形式为:
#if 常量表达式 代码#elif 常量表达式 代码#else 代码#endif
如,我们有一份标准的代码需要去适配多个个机型,这几个机型只有一个功能不同,其余的都一样。这个功能即为,当按键输入的值是1的时候,1号机型输出“Hello,Iam 1”,2号机型输出“Hello,I am 2”,其它机型输出“Hello,I am other”。那么上述这种情况除了输出不同之外,其余的功能都一模一样,因此我们根本不需要将这些代码分开管理,只需要使用条件编译,即可将这些代码合并。如图8-3-3所示。
图8-3-3 判断语句条件编译
  是否被定义
在编写C语言程序的时候,“是否被定义”这个条件编译往往是备用的最频繁的。实现是否被定义主要有两种方式。
第一种方式为,使用#if预处理和defined,!defined预处理相结合的方式。这里的defined为具体的判断符号是否被定义,一旦这个符号X被定义之后,使用defined(X),其最终结果会返回一个1,使用!defined(X),其最终结果会返回一个0。最终#if会根据返回值来选择代码是否要被编译。其使用形式为:
#if defined(符号) 程序语句#endif#if !defined(符号) 程序语句#endif
如,我们要判断符号DEBUG是否被定义,一旦被定义之后,就输出“Indebug mode”。如果DEBUG没有被定义,那么就定义RELEASE,如果RELASE被定义,那么就输出“In release mode”。具体代码如图8-3-4和图8-3-5所示。
图8-3-4 DEBUG被定义之后的输出

图8-3-5 DEBUG未被定义之后的输出

第二种方式,可以使用简写的#ifdef和#ifndef的形式来完成这个功能。
#if define(符号) <==> #ifdef 符号#if !define(符号) <==> #ifndef 符号
如图8-3-6中的代码,我们可以使用#ifdef和ifndef的形式,进行替换。
图8-3-6 #ifdef和#ifndef
最后大家再考虑下,为何每个源代码的.h文件中,都要使用#ifndef来检查标号是否被定义呢?如图8-3-7所示。
图8-3-7 .h文件常用的条件编译


(0)

相关推荐

  • C语言零基础教程之预处理和宏定义篇

    https://m.toutiao.com/is/JTpvb14/ 前言 上一章节主要讲解C语言文件操作,本章节主要讲解C语言预处理和宏定义. C语言预处理 在C语言中,预处理指令是在实际源代码编译之 ...

  • C语言 #pragma once - C语言零基础入门教程

    目录 一.#pragmaonce 宏简介 二.#pragmaonce 与 #ifndef 使用 1.#ifndef 2.#pragmaonce 三.#pragmaonce 与 #ifndef 区别 1 ...

  • 【C语言更新】熟练使用#define宏定义,中文编程不是梦

    文/Edward 前面我们在讲解程序编译的时候,编译器做的第一步就是预处理.C语言的预处理,其实是编译器在对整个工程编译之前做的一些文本性质的工作,例如我们经常使用的#include,这就是一个典型的 ...

  • 【C语言更新】结构体中实现函数成员以及回调函数

    文/Edward 前面说,结构体内部的成员变量可以是普通变量,数组,除了这些变量之外,还可以是指针,结构体,枚举,共用体等.综上所述的结构体内部成员中,我们可以发现一个结构体内部的成员竟然不包含函数. ...

  • 【C语言更新】结构体的定义及使用

    文/Edward 首先先思考一个问题,假设某一天你去了一家策划公司,接到了一个策划需求,比如为新上市的某款手机写一个市场推广的文案,并且在电脑上面打印出来.那么在写这个文案的时候,你肯定是会需要着重地 ...

  • 【C语言更新】指向函数的指针

    文/Edward   定义一个指向函数的指针 前面我们说了,指针的本质其实就是用来存放地址的变量,将一个对象的地址赋值给指针的过程被称为指向.我们前面已经讲过了指针指向变量,指针指向数组,指针指向字符 ...

  • 【C语言更新】指针函数

    文/Edward 今天开始,我们将来介绍C语言里面最让人头痛的两个东西,指针函数和指向函数的指针. 指针函数这个名字乍一看非常唬人,又是指针又是函数的,但是其实它并没有想象的那么复杂,千万别被它的名字 ...

  • 【C语言更新】C语言中字符串的操作

    文/Edward 在C语言的实际使用中,很多时候会去使用指针传递一些字符串,而C语言中也有很多关于字符串处理的库函数给大家来使用,只要在使用的时候包含头文件"string.h"就可 ...

  • 【C语言更新】字符串与指针

    文/Edward 之前我们说过关于字符的一些内容,字符的原理,归根到底就是将我们常用的一些字符根据编号整理成一张统一的表,每个字符对应着这个表里面的一个数值,这一过程就是字符的编码. 然而大多数时候我 ...

  • 【C语言更新】指向数组的指针

    文/Edward 前面我们在讲述数组的时候曾经说过,对于数组内部元素的引用方式主要有两种,一种就是使用方括号"[ ]"的下标索引,另一种就是之前提到过,但是没有细致讲述的指针引用. ...

  • 【C语言更新】指针变量的基本使用使用

    文/Edward 当我们将一个指针变量定义好之后,接下来需要考虑的问题就是如何来使用这个指针变量了.前面学习普通变量的时候,我们知道,一个变量的操作其实就是分成读和写两个步骤.而指针变量在平时使用的时 ...