2012-09-24 70 views
8

如果編譯器具有某種類型(例如ptrdiff_t)作爲嵌入式類型,我不想再次鍵入它。我知道下面的代碼不能正常工作,我預期。如何檢查某個類型是否已在C編譯器中定義?

#ifndef ptrdiff_t 
    typedef long int ptrdiff_t; 
#endif 

如何檢查某種類型在Ç編譯器已經定義?

+1

在你的具體情況。如果包含stddef.h,則應始終定義ptrdiff_t –

+2

能夠在代碼中測試特定定義的存在和屬性稱爲「反射」。腳本語言傾向於支持全面反思,部分原因是腳本語言相對容易。 .NET和JVM支持反射。但對於本地語言,支持運行時反射會需要大量額外的可執行代碼。一些編譯時反射可以在沒有這種開銷的情況下得到支持,但即使在編譯時C也幾乎沒有反射支持。 – Steve314

+0

使用像cmake或autotools這樣的工具 – szx

回答

1

在你的問題你是一個有點混亂2個不同的東西:

內置的類型,如intfloat,等等,這些都是STND類型和他們的定義是所有的編譯器。像__int64這樣的類型不是標準的,但是它們是在一些編譯器中定義的。你不需要做任何事情來使用它們。同時,如果你的代碼被定義或者沒有定義,你就無法弄清楚。這隻能從編譯器的文檔中找出來。你可以寫:

#ifdef MSVC 
     .... Microsoft specific code 
#else 
     .... Code for other compiler. 
#endif 

這種方法允許你創建類的compiler independent environment

除了內置的類型,還有類型來自標題。某些標題具有如下結構:

#ifndef ptrdiff_t_DEFINED 
    #define ptrdiff_t_DEFINED 
    typedef long int ptrdiff_t; 
#endif 

請注意,宏處理器defn與類型defn保持分開。您無法檢查類型是否已定義,但可以輕鬆檢查定義是否已定義。

代碼中包含了哪些標題,您可以自己選擇。這意味着這些定義不是in the compiler itself。它們在當前翻譯單元的定義集合中。對於編譯器來說,它們與您在自己的代碼中編寫的其他類型定義沒有多大區別。

一些編譯器或系統頭文件沒有像上面例子那樣的「守護defns」。在這種情況下,你唯一能做的就是跟蹤來自哪些標題,並且包括/不包括這些標題,maby使用你自己的#ifdef警衛圍繞着#include聲明。

5

沒有辦法做到這一點。在某些情況下,可能會有一個與您可以使用的類型同時定義的宏。

在你的特殊例子中,你可以使用#include <stddef.h>,它應該總是定義ptrdiff_t。

0

由於重新定義C語言保留構造通常會導致編譯時錯誤,因此無法對其進行一般檢查。 如果您對(學術/學習過程)感興趣,您可以編寫基本的編譯器通行證來檢查C程序中的異常/錯誤,以檢測是否保留類型。

4

正如其他人所說,沒有一個好的通用解決方案。類型名稱對預處理器不可見,所以您不能使用#ifdef來測試它們的存在。

雖然有一些部分解決方案,並且它們根據給定類型的需求來自何處而有所不同。

ISO C標準有幾種版本,分別在1990,1999和2011年發佈。每個新標準(理論上)取代並取代前一個標準,每個標準定義一些新的類型。例如,1999 C標準添加了標頭<stdbool.h><stdint.h>,以及類型bool,int32_t等。如果要使用bool類型,但仍希望代碼可移植到不支持C99的實現,則可以執行一些操作像:

#if defined(__STDC__) && __STDC_VERSION__ >= 199901L 
#include <stdbool.h> 
#else 
typedef enum { false, true } bool; 
#endif 

enum類型的行爲不完全相同像C99內置的bool類型,所以你需要在你如何使用它小心一點。

<stdint.h>中定義的uintptr_t類型是可選的。這是一種無符號類型,可以保存已轉換的指針值而不會丟失信息;沒有這種無符號類型的實現(比如,因爲指針大於任何整數類型)將不會提供它。你不能直接測試類型本身,但你可以測試,給其邊界的宏:

#include <stdint.h> 

#ifdef UINTMAX_MAX 
/* uintmax_t exists */ 
#else 
/* uintmax_t doesn't exist */ 
#endif 

您可能需要一個測試,包裝這爲__STDC____STDC_VERSION__如果您不能承擔C99或更好。

long long是一種預定義類型(不是庫的一部分),添加到C99中。同樣,你不能測試它直接,但你可以測試定義其邊界的宏:

#include <limits.h> 

#ifdef LLONG_MAX 
/* long long exists */ 
#else 
/* long long *probably* doesn't exist */ 
#endif 

最後,有一些事情你不能用C直接做的,但你可以做你程序的構建過程的一部分。例如,POSIX在POSIX專用標頭<unistd.h>(它是getpid()函數返回的進程標識符的類型)中定義了pid_t類型。無法有條件包括頭 - 但你可以寫一個小程序,它會失敗,如果標頭不存在編譯:

#include <unistd.h> 
pid_t dummy; 

作爲構建過程的一部分,嘗試編譯此文件。如果成功,則向

#define HAVE_PID_T 

添加一行到配置頭;如果失敗,追加像

#undef HAVE_PID_T 

在你的源代碼行,那麼你可以寫類似:

#include "config.h" 
#ifdef HAVE_PID_T 
#include <unistd.h> 
/* pid_t exists */ 
#else 
/* pid_t doesn't exist */ 
#endif 

GNU Autoconf提供了一種自動執行此類型的測試,但它被批評爲過於複雜和笨拙。

所有這一切都假定,一旦您確定某個類型是否存在,您可以對該信息做一些有用的事情。對於某些類型,如bool,可以實現幾乎相同的替代方法。另一方面,對於pid_t,可能不會有很好的回退,除非您僅處理所有處理進程的代碼。如果你的程序不能在沒有pid_tgetpid()的系統上工作,最好只編寫假定它們存在的代碼。如果你嘗試在沒有提供它們的系統上編譯你的代碼,它將立即無法編譯,這可能是你能做的最好的事情。

相關問題