如果编译器具有某种类型(例如 ptrdiff_t)作为嵌入式类型,我不想再次对其进行 typedef。我知道下面的代码不能像我预期的那样正常工作。
#ifndef ptrdiff_t
typedef long int ptrdiff_t;
#endif
如何检查某个类型是否已在C编译器中定义?
如果编译器具有某种类型(例如 ptrdiff_t)作为嵌入式类型,我不想再次对其进行 typedef。我知道下面的代码不能像我预期的那样正常工作。
#ifndef ptrdiff_t
typedef long int ptrdiff_t;
#endif
如何检查某个类型是否已在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
该类型的行为与 C99 的内置enum
类型不完全相同bool
,因此您需要小心使用它。
uintptr_t
中定义的类型<stdint.h>
是可选的。它是一种无符号类型,可以保存转换后的void*
指针值而不会丢失信息;没有这种无符号类型的实现(例如,因为指针比任何整数类型都大)不会提供它。您不能直接测试类型本身,但可以测试给出其边界的宏:
#include <stdint.h>
#ifdef UINTMAX_MAX
/* uintmax_t exists */
#else
/* uintmax_t doesn't exist */
#endif
如果您不能假设 C99 或更好,您可能需要将其包装在测试中__STDC__
。__STDC_VERSION__
该类型long long
是在 C99 中添加的预定义类型(不是库的一部分)。同样,您不能直接对其进行测试,但可以测试定义其边界的宏:
#include <limits.h>
#ifdef LLONG_MAX
/* long long exists */
#else
/* long long *probably* doesn't exist */
#endif
最后,有些事情你不能直接在 C 中做,但你可以作为程序构建过程的一部分来做。例如,POSIXpid_t
在特定于 POSIX 的标头中定义了一个类型<unistd.h>
(它是进程标识符的类型,由getpid()
函数返回)。您不能有条件地包含标头——但您可以编写一个小程序,如果标头不存在,该程序将无法编译:
#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
另一方面,对于,可能没有很好的回退,除非您简单地#ifdef
删除所有处理进程的代码。如果您的程序无法在没有pid_t
and的系统上运行,getpid()
最好只编写假设它们存在的代码。如果您尝试在不提供代码的系统上编译代码,它将立即无法编译,这可能是您能做的最好的事情。
一般没有办法做到这一点。在某些情况下,可能存在与您可以使用的类型同时定义的宏。
在您的特定示例中,您可以#include <stddef.h>
,它应该始终定义 ptrdiff_t。
在您的问题中,您有点混淆了两件事:
有内置类型,如int
,float
等。这些是标准类型,它们被定义为所有编译器。类似的类型__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
请注意,宏处理器定义与类型定义是分开的。您无法检查类型是否已定义,但您可以轻松检查是否定义了宏定义。
您自己决定的代码中包含哪些标头。这意味着这些定义不是in the compiler itself
. 它们位于当前翻译单元的定义集中。对于编译器,它们与您在自己的代码中编写的其他类型定义几乎没有区别。
一些编译器或系统头文件没有像上面示例中的“保护定义”。在这种情况下,您唯一能做的就是跟踪即将到来的标头并包含/不包含这些标头,可能在语句#ifdef
周围使用您自己的警卫。#include
由于重新定义 C 语言保留结构通常会导致编译时错误,因此一般无法对其进行检查。如果您有兴趣(对于学术/学习过程),您可以编写一个基本的编译器通过来检查您的 C 程序上的异常/错误,以检测一个类型是否被保留。