C條件編譯

2021-10-08 21:53:09 字數 4767 閱讀 3290

在 c 語言中,條件編譯指令可以實現源**的部分編譯功能,可以根據表示式的值或者某個特定的巨集來確定編譯條件,以決定編譯哪些**,不編譯哪些。

在 c 語言中,乙個檔案中可以包含多個標頭檔案,而標頭檔案之間又是可以相互引用的,這將引起乙個檔案中可能間接多次包含某個標頭檔案,從而導致了某些標頭檔案被重複引用多次。

例如,有 3 個檔案 a.h、b.h 和 c.h,其中 b 檔案中包含了 a.h,而 c 檔案中又分別包含了 a.h 和 b.h 兩個檔案。於是問題出來了,由於巢狀包含檔案的原因,標頭檔案 a.h 被兩次包含在原始檔 c 中。在這裡,如果標頭檔案中沒有防止多次編譯的語句,有可能會引起如下兩種後果:

在 c 語言中,避免同乙個標頭檔案被多次包含、重複引用,最常用也是最簡單的方法就是利用「#ifndef/#define/#endif」結構產生預處理塊來防止標頭檔案被重複引用。如下面的示例**所示:

#ifndef __headername_h__

#define __headername_h__

/*宣告、定義語句*/

#endif

在上面的預處理塊中,當第一次引用(include)標頭檔案時,由於「__headername_h__」還沒有被巨集定義(define)過,即滿足「#ifndef__headername_h__」,從而執行「#define__headername_h__」以及其他內容。

如果因為編碼者的不小心或者巢狀包含等原因造成了這個標頭檔案被多次引用(include),那麼「#ifndef__headername_h__」判斷條件將在第二次引用(include)標頭檔案時得不到滿足,因此不執行後面的內容,直接跳到「#endif」。

通過「#ifndef/#define/#endif」結構產生預處理塊,雖然能夠避免同乙個標頭檔案被多次包含和重複引用,但也存在乙個致命的缺點,那就是一旦一不小心在不同標頭檔案中定義了相同的巨集名,問題就比較麻煩了。比如,可能會導致明明看到存在標頭檔案,而編譯器卻硬說找不到宣告等問題。為了避免這種情況,保證巨集名的唯一性,建議按照 google 公司的建議,標頭檔案基於其所在專案源**樹的全路徑進行命名。命名格式為:

___h_

其中,project 表示專案名稱,path 表示標頭檔案相對路徑,file 表示檔名,再以「_h_」作為字尾。比如,在專案 cashregister 中,現在該專案所在目錄下的乙個名為 xml 的子資料夾下的乙個 parser 標頭檔案,則巨集定義如下:

#ifndef cashregister_xml_parser_h_

#define cashregister_xml_parser_h_

/*宣告、定義語句*/

#endif

當然,基於命名習慣原因,也可以這樣來寫:

#ifndef _cashregister_xml_parser_h_

#define _cashregister_xml_parser_h_

/*宣告、定義語句*/

#endif

這裡需要注意的是,由於編譯器在每次編譯時都需要開啟標頭檔案才能判定是否有重複定義,因此在編譯大型專案時,「#ifndef」會使編譯時間相對較長。

除此之外,你還可以使用「#pragma once」方式來防止標頭檔案被重複引用,該方式一般由編譯器提供,可以保證同乙個檔案不會被包含多次。但這裡需要特別說明的是,該方式受編譯器的限制,有些編譯器並不支援該指令,因此在相容性方面表現得不是很好。這裡建議為了**的相容性,寧肯降低一些編譯效能,還是使用「#ifndef/#define/#endif」結構。

前面已經說過,條件編譯指令可以使編譯器按不同的條件編譯不同的程式部分,因而產生不同的目標**檔案。這對於程式的移植和除錯是很有用的,尤其是針對於跨平台程式移植的時候。在 c 語言中,主要有如下條件編譯指令。

1、#if指令

該指令檢測表示式值是否為真。如果表示式的值為真,則編譯後面的**直到出現 #else、#elif 或 #endif 為止,否則不編譯。

2、#endif指令

該指令用於終止 #if 指令。

3、#else指令

該指令用於 #if 指令之後,當前面的 #if 指令的條件不為真時,就編譯 #else 後面的**。

4、#elif指令

該指令綜合了 #else 和 #if 指令的作用。下面的示例**演示了 #if、#else、#elif 與 #endif 的組合使用情況。

#if os==1

printf("v1.0");

#elif os==2

printf("v2.0");

#else

printf("未知");

#endif

5、#ifdef 和 #ifndef 指令

相對於 #if 指令(檢測表示式的值是否為真),#ifdef 和 #ifndef 指令用於檢測指令關鍵字後面的巨集名稱是否已經定義。其中,#ifdef 指令表示如果巨集已經被定義,那麼它的檢測結果為真,否則返回假;而 #ifndef 指令的含義正好與 #ifdef 指令相反,它表示如果巨集未被定義,那麼它的檢測結果為真,否則為假。

在一般情況下,條件編譯指令組合主要有如下幾種形式。

1) 第一種形式如下:

#ifdef 巨集名稱

/*程式段1*/

#else

/*程式段2*/

#endif

它表示如果巨集名稱已經定義,則對程式段 1 進行編譯;否則對程式段 2 進行編譯。如果沒有程式段 2,則可以省略「#else」,如下所示:

#ifdef 巨集名稱

/*程式段1*/

#endif

2) 第二種形式如下:

#ifndef 巨集名稱

/*程式段1*/

#else

/*程式段2*/

#endif

#ifndef 指令的含義正好與 #ifdef 指令相反,因此它表示如果巨集名稱未被定義,則對程式段 1 進行編譯,否則對程式段 2 進行編譯。

3) 第三種形式如下:

#if 表示式

/*程式段1*/

#else

/*程式段2*/

#endif

它表示如果表示式的值為真(非 0),則對程式段 1 進行編譯,否則對程式段 2 進行編譯。如果有多個(兩個以上)條件,則可以用 #elif 指令,如下所示:

#if 表示式

/*程式段1*/

#elif 表示式

/*程式段2*/

#elif 表示式

/*程式段3*/

#else

/*程式段4*/

#endif

在 c 語言中,除了「#ifdef」和「#ifndef」指令之外,還可以使用 defined 判斷識別符號是否定義過。實際上,「#if defined」等價於「#ifdef」,而「#if!defined」等價於「#ifndef」。例如,下面的示例**就演示了如何使用 defined 來避免重複包含標頭檔案引起的重複定義問題:

#if !defined(_cashregister_xml_parser_h_)

#define _cashregister_xml_parser_h_

/*宣告、定義語句*/

#endif

它實際上等價於下面的**:

#ifndef _cashregister_xml_parser_h_

#define _cashregister_xml_parser_h_

/*宣告、定義語句*/

#endif

當然,從上面的示例來看,或許看不出來使用 defined 有任何的優勢。但是,在處理雙重和多重判斷時,defined 的優勢就顯現出來了,如下面的**所示:

#ifndef _cashregister_xml_parser_h_

#ifndef _cashregister_xml_transform_h_

#ifndef _cashregister_xml_decode_h_

/************/

#endif

#endif

#endif

從上面的**中可以看出,使用「#ifdef」和「#ifndef」指令一次只能同時檢測乙個巨集是否定義。如果需要檢測多個巨集,則需將「#ifdef」和「#ifndef」指令重複複製多次,這樣看起來很不友好。但是,如果這裡使用 defined,那就簡單多了,如下面的**所示:

#if !defined(_cashregister_xml_parser_h_)&&!defined(_cashregister_xml_transform_h_)&&!defined(_cashregister_xml_decode_h_)

/************/

#endif

其實,defined 的使用是非常普及的,在一些常見的 c 語言標準庫中也隨處可見,例如:

#if (!defined __strict_ansi__ && !defined _isoc99_source && \

!defined _posix_source && !defined _posix_c_source && \

!defined _xopen_source && !defined _bsd_source && \

!defined _svid_source)

# define _bsd_source 1

# define _svid_source 1

#endif

因此,這裡建議使用 defined。因為即使當前**使用的是簡單的條件編譯,以後在維護或公升級時也可能會增加,這樣也可以提高程式的可維護性。

C 條件編譯

條件編譯 按照不同的編譯條件去編譯不同的程式 從而使乙個源程式在不同的編譯條件下生成不同的目標程式。c 提供幾種條件編譯指令,以乙個表示式或者某個巨集是否被定義的條件作為編譯條件。指令 用途 空指令,無任何效果 include 包含乙個源 檔案 define 定義巨集 undef 取消已定義的巨集 ...

C 條件編譯

一 條件編譯方法 1 if,elif,else,endif 使用條件編譯指令,如果滿足 if後面的條件,就編譯 if和 endif之間的程式段,否則不編譯。二 編譯此段 時是否有某個巨集通過 ifdef,else,endif或 ifndef,else,endif 如果乙個c源程式在不同計算機系統上執...

C 條件編譯

c 的預處理器指令從來不會轉化為可執行 的命令,但是會影響編譯過程的各個方面,常用的預處理器指令有 define undef if,elif,else和 endif等等,下面介紹c 中使用 define進行條件編譯的例項。c 中條件編譯指令用於按條件包含或排除原始檔中的某些部分。在visual st...