12

如果编译器具有某种类型(例如 ptrdiff_t)作为嵌入式类型,我不想再次对其进行 typedef。我知道下面的代码不能像我预期的那样正常工作。

#ifndef ptrdiff_t 
    typedef long int ptrdiff_t;
#endif

如何检查某个类型是否已在C编译器中定义?

4

4 回答 4

6

正如其他人所说,对此没有好的通用解决方案。类型名称对预处理器不可见,因此您不能用于#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_tand的系统上运行,getpid()最好只编写假设它们存在的代码。如果您尝试在不提供代码的系统上编译代码,它将立即无法编译,这可能是您能做的最好的事情。

于 2012-09-24T20:51:53.270 回答
6

一般没有办法做到这一点。在某些情况下,可能存在与您可以使用的类型同时定义的宏。

在您的特定示例中,您可以#include <stddef.h>,它应该始终定义 ptrdiff_t。

于 2012-09-24T03:42:52.527 回答
2

在您的问题中,您有点混淆了两件事:

有内置类型,如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

于 2012-09-24T05:39:12.130 回答
0

由于重新定义 C 语言保留结构通常会导致编译时错误,因此一般无法对其进行检查。如果您有兴趣(对于学术/学习过程),您可以编写一个基本的编译器通过来检查您的 C 程序上的异常/错误,以检测一个类型是否被保留。

于 2012-09-24T04:07:54.817 回答