Как я могу проверить, что определенный тип уже определен в компиляторе C?
Если у компилятора есть определенный тип (например, ptrdiff_t) как внедренный тип, я не хочу его снова вводить. Я знаю, что ниже код работает неправильно, что я ожидал.
#ifndef ptrdiff_t
typedef long int ptrdiff_t;
#endif
Как я могу проверить, что определенный тип уже определен в компиляторе C?
Ответы
Ответ 1
В своем вопросе вы немного путаете две разные вещи:
Существуют встроенные типы, такие как 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
.
Ответ 2
Нет никакого способа сделать это вообще. В некоторых случаях может быть макрос, который определяется одновременно с типом, который вы можете использовать.
В вашем конкретном примере вы можете #include <stddef.h>
, который всегда должен определять ptrdiff_t.
Ответ 3
Как уже говорили другие, нет хорошего общего решения для этого. Имена типов не видны препроцессору, поэтому вы не можете использовать #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
, поэтому вам нужно быть немного осторожнее в его использовании.
Тип uintptr_t
, определенный в <stdint.h>
, является необязательным. Это тип без знака, который может содержать преобразованное значение указателя void*
без потери информации; реализация, у которой нет такого типа без знака (скажем, потому что указатели больше, чем у любого целочисленного типа), не предоставит его. Вы не можете напрямую проверить сам тип, но вы можете проверить макросы, которые дают его границы:
#include <stdint.h>
#ifdef UINTMAX_MAX
/* uintmax_t exists */
#else
/* uintmax_t does not exist */
#endif
Возможно, вам понадобится обернуть это в тесте для __STDC__
и __STDC_VERSION__
, если вы не можете принять C99 или выше.
Тип long long
является предопределенным типом (не является частью библиотеки), добавленным в C99. Опять же, вы не можете проверить это напрямую, но вы можете проверить макросы, которые определяют его границы:
#include <limits.h>
#ifdef LLONG_MAX
/* long long exists */
#else
/* long long *probably* does not exist */
#endif
Наконец, есть вещи, которые вы не можете сделать непосредственно в C, но которые вы можете сделать как часть процесса сборки вашей программы. Например, POSIX определяет тип pid_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 does not exist */
#endif
GNU Autoconf предоставляет способ автоматизации такого рода испытаний, но его критикуют за то, что он слишком сложный и громоздкий.
Все это предполагает, что, как только вы определили, существует ли тип, вы можете сделать что-то полезное с этой информацией. Для некоторых типов, таких как bool
, вы можете реализовать почти эквивалентную альтернативу. Для pid_t
, с другой стороны, вероятно, не будет хорошего отступления, если вы просто #ifdef
не удалите весь код, который имеет дело с процессами. Если ваша программа просто не будет работать в системе, в которой нет pid_t
и getpid()
, лучше всего написать код, который предполагает, что они существуют. Если вы попытаетесь скомпилировать свой код в системе, которая его не предоставляет, он сразу же не сможет скомпилироваться, и это может быть лучшим решением, которое вы можете сделать.
Ответ 4
Поскольку переопределение зарезервированной конструкции языка C обычно приводит к ошибке времени компиляции, ее вообще невозможно проверить.
Если вам интересно (для академического/учебного процесса), вы можете написать базовый прогон компилятора, чтобы проверить исключения/ошибки в вашей программе на C, чтобы определить, зарезервирован ли тип или нет.