1、条件编译书写时的排版问题
1.1 什么是条件编译的排版
就是#if、#elif、#else、#endif关键字的对齐、缩进等等,这些就是“条件编译”的排版。
1.2 为什么介绍条件编译的书写排版
两个原因:
(1)写出更容易阅读的条件编译。
与{ ... }的这种书写方式对比起来,条件编译的#if ... #endif的这种书写方式不太容易阅读,此时如果你又不注意排版问题,就会使得阅读变得很糟糕。
(2)了解了排版规则后,有利于理解别人写的条件编译
1.3 条件编译的排版
(1)条件编译没有互相嵌套时
所有条件编译关键字语句顶格左对齐。
#if 0 ... #elif 1 ... #endif #ifdef NUM .. #else ... #endif
(2)如果条件编译之间有嵌套时
嵌套的条件编译需要进行缩进,如果里面有包含#define、#include的话,define、include也需要缩进。
我们举两个stdio.h中的真实案例。
1)例子1
#if !defined __need_FILE && !defined __need___FILE # define _STDIO_H 1 # include <features.h> __BEGIN_DECLS # define __need_size_t # define __need_NULL # include <stddef.h> # include <bits/types.h> # define __need_FILE # define __need___FILE #endif /* Don't need FILE. */
注意缩进方式,#是不用缩进,#和define、include中间隔有空格是没有问题的。
通过所缩进可以非常明显的看出包含关系和配对关系。
不过这个例子中没有嵌套其它的条件编译。
2)例子2
#if defined __USE_XOPEN || defined __USE_XOPEN2K8 # ifdef __GNUC__ # ifndef _VA_LIST_DEFINED typedef _G_va_list va_list; //正常代码(非预编译代码),不需要缩进 # define _VA_LIST_DEFINED # endif # else # include <stdarg.h> # endif #endif
这个例子中条件编译就存在非常复杂的嵌套关系,如果不进行缩进的话,你将会完全无法理清配对关系,如果无法理清配对关系,自然你也就无法理清这些条件编译的逻辑关系,阅读类似这样的源码时,你直接就发蒙了。
通过这个例子我们可以感受到,如果你不清楚条件编译的排版的话,当你看到这些玩意时会很头晕。
(3)条件编译在函数体外时
条件编译关键字语句顶格左对齐。
Linux内核源码的例子。
1)例子1:
#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315) static void SiS_ShortDelay(struct SiS_Private *SiS_Pr, unsigned short delay) { while(delay--) { SiS_GenericDelay(SiS_Pr, 66); } } #endif
只有里面包含的函数代码无需缩进,正常格式写即可。
(4)条件编译在函数体内时
没有嵌套时定格左对齐即可。
void SiS_IsVAMode(struct SiS_Private *SiS_Pr) { #ifdef CONFIG_FB_SIS_315 unsigned short flag; if(SiS_Pr->ChipType >= SIS_315H) { flag = SiS_GetReg(SiS_Pr->SiS_P3d4,0x38); if((flag & EnableDualEdge) && (flag & SetToLCDA)) return true; } #endif return false; }
如果有条件编译嵌套的话,按照之前讲的进行缩进即可。
1.4 是不是学会了条件编译关键字以及排版问题,就一定能看懂源码中的条件编译了呢?
当然不是的,如果想看懂某个条件编译想干什么,还有一个关键就是要能看懂“条件编译”的判断条件。
#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315) static void SiS_WaitRetrace2(struct SiS_Private *SiS_Pr, unsigned short reg) { unsigned short watchdog; watchdog = 65535; while((SiS_GetReg(SiS_Pr->SiS_Part1Port,reg) & 0x02) && --watchdog); watchdog = 65535; while((!(SiS_GetReg(SiS_Pr->SiS_Part1Port,reg) & 0x02)) && --watchdog); } #endif
想要理解条件编译#if defined(CONFIG_FB_SIS_300) || defined(CONFIG_FB_SIS_315),你需要理解CONFIG_FB_SIS_300和CONFIG_FB_SIS_315宏。
如果想要理解这两个宏的话,这就与源码逻辑有关了。
但是#if、#ifdef等条件编译关键字,以及条件编译的排版是基础,你连这个基础都不行,想要弄懂源码中的条件编译那就更不容易了。
疑问:分析源码时,是不是源码中所有的条件编译代码都需要搞明白呢?
当然不是的,根据你自己阅读源码的需要,搞明白必须要弄明白的条件编译即可。
1.5 再说下有关条件编译的缩进
在有些源码中,条件编译缩进可能是如下样子。
#if defined __USE_XOPEN || defined __USE_XOPEN2K8 #ifdef __GNUC__ #ifndef _VA_LIST_DEFINED typedef _G_va_list va_list; //正常代码(非预编译代码),不需要缩进 #define _VA_LIST_DEFINED #endif #else #include <stdarg.h> #endif #endif
不管是前面介绍的缩进方式,还是目前这种缩进方式,其实都可以,这两种缩进方式,不同的源码都有使用,至于我们自己使用哪种,可以跟着当前你自己所使用源码的风格走,也可以根据自己的喜好走。