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

lqe*_*qez 10 c types typedef c-preprocessor

如果编译器具有某种类型(例如ptrdiff_t)作为嵌入类型,我不想再次键入它.我知道下面的代码不能正常工作.

#ifndef ptrdiff_t 
    typedef long int ptrdiff_t;
#endif
Run Code Online (Sandbox Code Playgroud)

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

Vau*_*ato 6

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

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


Kei*_*son 6

正如其他人所说,对此没有很好的通用解决方案。类型名称对预处理器不可见,因此您不能使用它#ifdef来测试它们的存在。

但是,有许多局部解决方案,它们取决于给定类型的要求来自何处。

ISO C标准有多个版本,分别于1990年,1999年和2011年发布。从理论上讲,每个新标准都将取代并替代以前的标准,并且每个标准都定义了一些新类型。例如,1999 C标准添加了标头<stdbool.h>和和<stdint.h>以及类型boolint32_t等。如果要使用bool类型,但仍希望代码可移植到不支持C99的实现中,则可以执行以下操作:

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

enum类型的行为与C99的内置类型并不完全一样bool,因此您在使用它时需要格外小心。

uintptr_t定义的type <stdint.h>是可选的。这是一个无符号类型,可以保存转换后的void*指针值而不会丢失信息。没有此类无符号类型的实现(例如,因为指针大于任何整数类型)将无法提供该实现。您不能直接测试类型本身,但是可以测试给出其界限的宏:

#include <stdint.h>

#ifdef UINTMAX_MAX
/* uintmax_t exists */
#else
/* uintmax_t doesn't exist */
#endif
Run Code Online (Sandbox Code Playgroud)

如果您不能假定C99或更高版本__STDC____STDC_VERSION__则可能需要将其包装在测试中。

该类型long long是C99中添加的预定义类型(不是库的一部分)。同样,您不能直接对其进行测试,但是可以对定义其范围的宏进行测试:

#include <limits.h>

#ifdef LLONG_MAX
/* long long exists */
#else
/* long long *probably* doesn't exist */
#endif
Run Code Online (Sandbox Code Playgroud)

最后,有些事情您不能直接在C中完成,但是可以作为程序构建过程的一部分来完成。例如,POSIX pid_t在特定于POSIX的标头中定义一种类型<unistd.h>(这是该getpid()函数返回的进程标识符的类型)。您不能有条件地包含标头-但是您可以编写一个小程序,如果标头不存在,该程序将无法编译:

#include <unistd.h>
pid_t dummy;
Run Code Online (Sandbox Code Playgroud)

在构建过程中,请尝试编译该文件。如果成功,请添加如下一行

#define HAVE_PID_T
Run Code Online (Sandbox Code Playgroud)

到配置头;如果失败,则添加一行

#undef HAVE_PID_T
Run Code Online (Sandbox Code Playgroud)

然后,在源代码中,您可以编写如下内容:

#include "config.h"
#ifdef HAVE_PID_T
#include <unistd.h>
/* pid_t exists */
#else
/* pid_t doesn't exist */
#endif
Run Code Online (Sandbox Code Playgroud)

GNU Autoconf提供了一种自动化这种测试的方法,但是由于过于复杂和笨拙而受到批评。

所有这些都假定,一旦确定类型是否存在,就可以对该信息进行一些有用的操作。对于某些类型,例如bool,您可以实现几乎等效的替代方法。对于pid_t,而另一方面,也有可能不是一个很好的回退,除非你根本#ifdef出所有的代码与进程的交易。如果您的程序无法在没有pid_tand 的系统上运行,那么getpid()最好编写假定它们存在的代码。如果您尝试在不提供代码的系统上编译代码,那么它将立即无法编译,这可能是您最好的选择。


Kir*_*lev 2

在你的问题中,你有点混淆两件不同的事情:

有内置类型,如intfloat等。这些是标准类型,它们是由所有编译器定义的。诸如此类的类型__int64后来被引入并标准化。这意味着它们在所有最新的编译器中定义,但仅在某些较旧的编译器中定义。您无需执行任何操作即可使用它们。同时,您无法在代码中确定它们是否已定义。这只能从编译器的文档中弄清楚。你可以写:

#ifdef MSVC
       .... Microsoft specific code
#else
       .... Code for other compiler.
#endif
Run Code Online (Sandbox Code Playgroud)

这种方法允许您创建某种compiler independent environment.

除了内置类型之外,还有来自标头的类型。一些标头具有如下结构:

#ifndef ptrdiff_t_DEFINED
    #define ptrdiff_t_DEFINED
    typedef long int ptrdiff_t;
#endif
Run Code Online (Sandbox Code Playgroud)

请注意,宏处理器定义与类型的定义分开。您无法检查类型是否已定义,但您可以轻松检查宏定义是否已定义。

您自己决定代码中包含哪些标头。这意味着这些定义不是in the compiler itself。它们位于当前翻译单元的定义集中。对于编译器来说,它们与您在自己的代码中编写的其他类型定义几乎没有什么区别。

某些编译器或系统标头没有像上面的示例中那样的“保护定义”。在这种情况下,您唯一能做的就是跟踪即将到来的标头,并包含/不包含这些标头,可以在语句#ifdef周围使用您自己的防护措施#include