1.4.7 優雅編寫條件編譯代碼
條件編譯在C代碼中十分常見。條件編譯的常見用途有如下3種。
(1)當我們要將自己開發的軟件運行在不同的操作系統中時,由于底層標準庫在不同操作系統中的實現存在差異,我們需要使用條件編譯來處理這種差異。
(2)類似地,當我們要針對不同的處理器或處理器架構編寫特定的代碼實現時,也需要使用條件編譯。
(3)當我們要通過編譯時配置選項來控制軟件的功能(比如通過配置選項控制包含哪些功能模塊)時,會經常使用條件編譯。
如下代碼來自MiniGUI的頭文件,用于判斷如何確定64位的整數數據類型,其中給出了條件編譯的常見用法:
/* Figure out how to support 64-bit datatypes */ #if !defined(__STRICT_ANSI__) # if defined(__GNUC__) # define MGUI_HAS_64BIT_TYPE long long # endif # if defined(__CC_ARM) # define MGUI_HAS_64BIT_TYPE long long # endif # if defined(_MSC_VER) # define MGUI_HAS_64BIT_TYPE __int64 # endif #endif /* !__STRICT_ANSI__ */ /* The 64-bit datatype isn't supported on all platforms */ #ifdef MGUI_HAS_64BIT_TYPE typedef unsigned MGUI_HAS_64BIT_TYPE Uint64; typedef signed MGUI_HAS_64BIT_TYPE Sint64; #else /* This is really just a hack to prevent the compiler from complaining */ typedef struct { Uint32 hi; Uint32 lo; } Uint64, Sint64; #endif
如上述代碼所示,條件編譯會嚴重割裂代碼的連續性,從而極大破壞代碼的可讀性。因此,我們應該盡量避免使用條件編譯。當然,由于C語言主要用來開發底層基礎軟件,因此C程序員難免會因為上面所說的3種用途而使用條件編譯。為此,下面是4條可供C程序員參考的處理原則。
第一,使用恰當的注釋說明條件編譯代碼塊的作用,如下所示:
#ifdef foo ... #else /* foo */ ... #endif /* not foo */ #ifdef foo ... #endif /* foo */ #ifndef bar ... #else /* not bar */ ... #endif /* bar */ #ifndef bar ... #endif /* not bar */
上述代碼在#else
和#endif
代碼行的末尾,使用了恰當的注釋來說明條件編譯代碼塊所對應的條件。
第二,在嵌套條件編譯時,恰當地使用縮進來表示嵌套關系,如下所示:
#ifndef NULL # ifdef __cplusplus # define NULL (0) # else # define NULL ((void *)0) # endif #endif /* not defined NULL */
第三,避免使用過長的條件編譯代碼塊,確保一個條件編譯代碼塊不超過常見編輯器的最大行數(25~50行)。
第四,使用構建系統生成器提供的方法實現對軟件功能的控制,避免使用條件編譯。比如,在實現一個跨平臺的文件系統操作接口時,需要考慮到不同操作系統之間(尤其是Windows操作系統和類UNIX操作系統之間)的巨大差異。這時,我們可以借助構建系統生成器,根據當前所要構建的目標系統,生成對應的構建文件,從而針對不同的目標平臺編譯不同的源文件。為此,我們可以將針對類UNIX操作系統(如Linux和macOS)的代碼組織到一個源文件中,如filesystem-unix-like.c
;而將針對Windows操作系統的代碼組織到另一個源文件中,如filesystem-windows.c
。這樣就可以避免在源文件中使用大段的條件編譯。
知識點:構建系統生成器
構建系統(build system)生成器(generator)是用于生成構建C、C++項目等所使用的構建系統的工具。這里的構建系統通常由一組Makefile組成。因此,我們可以將構建系統生成器理解成Makefile生成器。常見的構建系統生成器有GNU Autotools、CMake、Meson等。成熟的構建系統生成器通常具有跨平臺的特征,可以幫助我們針對不同的平臺組織我們的源文件,并按照目標構建系統的特性自動生成一些宏,從而提升代碼的可移植性。
有關構建系統生成器的內容,我們將在第5章做詳細闡述。
- 從零構建知識圖譜:技術、方法與案例
- Learning NServiceBus(Second Edition)
- 解構產品經理:互聯網產品策劃入門寶典
- SpringMVC+MyBatis快速開發與項目實戰
- 程序員數學:用Python學透線性代數和微積分
- 神經網絡編程實戰:Java語言實現(原書第2版)
- 網絡爬蟲原理與實踐:基于C#語言
- 區塊鏈底層設計Java實戰
- Learning Probabilistic Graphical Models in R
- 輕松上手2D游戲開發:Unity入門
- Arduino可穿戴設備開發
- 自學Python:編程基礎、科學計算及數據分析(第2版)
- Web程序設計:ASP.NET(第2版)
- Java EE程序設計與開發實踐教程
- Swift iOS Programming for Kids