如果編譯器具有某種類型(例如ptrdiff_t)作爲嵌入式類型,我不想再次鍵入它。我知道下面的代碼不能正常工作,我預期。如何檢查某個類型是否已在C編譯器中定義?
#ifndef ptrdiff_t
typedef long int ptrdiff_t;
#endif
如何檢查某種類型在Ç編譯器已經定義?
如果編譯器具有某種類型(例如ptrdiff_t)作爲嵌入式類型,我不想再次鍵入它。我知道下面的代碼不能正常工作,我預期。如何檢查某個類型是否已在C編譯器中定義?
#ifndef ptrdiff_t
typedef long int ptrdiff_t;
#endif
如何檢查某種類型在Ç編譯器已經定義?
在你的問題你是一個有點混亂2個不同的東西:
內置的類型,如int
,float
,等等,這些都是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
聲明。
沒有辦法做到這一點。在某些情況下,可能會有一個與您可以使用的類型同時定義的宏。
在你的特殊例子中,你可以使用#include <stddef.h>
,它應該總是定義ptrdiff_t。
由於重新定義C語言保留構造通常會導致編譯時錯誤,因此無法對其進行一般檢查。 如果您對(學術/學習過程)感興趣,您可以編寫基本的編譯器通行證來檢查C程序中的異常/錯誤,以檢測是否保留類型。
正如其他人所說,沒有一個好的通用解決方案。類型名稱對預處理器不可見,所以您不能使用#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_t
和getpid()
的系統上工作,最好只編寫假定它們存在的代碼。如果你嘗試在沒有提供它們的系統上編譯你的代碼,它將立即無法編譯,這可能是你能做的最好的事情。
在你的具體情況。如果包含stddef.h,則應始終定義ptrdiff_t –
能夠在代碼中測試特定定義的存在和屬性稱爲「反射」。腳本語言傾向於支持全面反思,部分原因是腳本語言相對容易。 .NET和JVM支持反射。但對於本地語言,支持運行時反射會需要大量額外的可執行代碼。一些編譯時反射可以在沒有這種開銷的情況下得到支持,但即使在編譯時C也幾乎沒有反射支持。 – Steve314
使用像cmake或autotools這樣的工具 – szx