官术网_书友最值得收藏!

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章做詳細闡述。

主站蜘蛛池模板: 明水县| 神池县| 成武县| 宿迁市| 横峰县| 安图县| 罗定市| 常宁市| 屯昌县| 新郑市| 海淀区| 大理市| 长沙市| 巴中市| 陈巴尔虎旗| 崇州市| 田东县| 乐昌市| 绍兴市| 龙江县| 阿瓦提县| 榕江县| 千阳县| 樟树市| 延庆县| 方山县| 吉安市| 黑水县| 建平县| 青川县| 额敏县| 固阳县| 交口县| 溧水县| 濮阳县| 富源县| 大港区| 类乌齐县| 车致| 金华市| 泉州市|