Подтвердить что ты не робот

Как проверить, имеет ли переменная определенный тип (сравнивают два типа) в C?

В C (не С++/С#), как проверить, имеет ли переменная определенный тип?

Например, что-то вроде этого:

double doubleVar;
if( typeof(doubleVar) == double ) {
    printf("doubleVar is of type double!");
}

Или более общий: как сравнить два типа, чтобы compare(double1,double2) оценил значение true, а compare(int,double) будет оцениваться как false. Также я хотел бы сравнить структуры другого состава.

В принципе, у меня есть функция, которая работает с переменными типа "struct a" и "struct b". Я хочу сделать одно с переменными "struct a", а другое с переменными "struct b". Так как C не поддерживает перегрузку, а указатель void потеряет информацию о типе, мне нужно проверить тип. Кстати, какой смысл иметь оператор typeof, если вы не можете сравнивать типы?


Метод sizeof кажется практическим решением для меня. Спасибо за вашу помощь. Мне все еще кажется, что это немного странно, поскольку типы известны во время компиляции, но если я представляю процессы на машине, которые я вижу, то почему информация не хранится в терминах типов, а скорее по размеру байта. Размер - это единственное, что действительно важно, кроме адресов.

4b9b3361

Ответ 1

Получение типа переменной на данный момент возможно на C11 с общим выбором _Generic. Он работает во время компиляции.

Синтаксис немного похож на switch. Здесь образец (из этого ответа):

#define typename(x) _Generic((x),                                                 \
        _Bool: "_Bool",                  unsigned char: "unsigned char",          \
         char: "char",                     signed char: "signed char",            \
    short int: "short int",         unsigned short int: "unsigned short int",     \
          int: "int",                     unsigned int: "unsigned int",           \
     long int: "long int",           unsigned long int: "unsigned long int",      \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
        float: "float",                         double: "double",                 \
  long double: "long double",                   char *: "pointer to char",        \
       void *: "pointer to void",                int *: "pointer to int",         \
      default: "other")

Чтобы на самом деле использовать его для проверки типа рутинга вручную, вы можете определить enum со всеми типами, которые вы ожидаете, примерно так:

enum t_typename {
    TYPENAME_BOOL,
    TYPENAME_UNSIGNED_CHAR,
    TYPENAME_CHAR,
    TYPENAME_SIGNED_CHAR,
    TYPENAME_SHORT_INT,
    TYPENAME_UNSIGNED_CHORT_INT,
    TYPENAME_INT,
    /* ... */
    TYPENAME_POINTER_TO_INT,
    TYPENAME_OTHER
};

И затем используйте _Generic для сопоставления типов с этим enum:

#define typename(x) _Generic((x),                                                       \
        _Bool: TYPENAME_BOOL,           unsigned char: TYPENAME_UNSIGNED_CHAR,          \
         char: TYPENAME_CHAR,             signed char: TYPENAME_SIGNED_CHAR,            \
    short int: TYPENAME_SHORT_INT, unsigned short int: TYPENAME_UNSIGNED_SHORT_INT,     \
          int: TYPENAME_INT,                     \
    /* ... */                                    \
        int *: TYPENAME_POINTER_TO_INT,          \
      default: TYPENAME_OTHER)

Ответ 2

C не поддерживает эту форму интроспекции типа. То, что вы просите, невозможно в C (по крайней мере, без расширений для компилятора, однако это возможно в С++).

В общем, с C вы должны знать типы своей переменной. Поскольку каждая функция имеет конкретные типы для своих параметров (за исключением varargs, я полагаю), вам не нужно проверять тело функции. Единственный оставшийся случай, который я вижу, находится в макрообъекте, и, ну, макросы C на самом деле не настолько мощные.

Кроме того, обратите внимание, что C не сохраняет информацию о типе во время выполнения. Это означает, что даже если гипотетически существует расширение сравнения типов, оно будет работать только тогда, когда типы известны во время компиляции (т.е. Не будет работать, чтобы проверить, указывают ли два void * на один и тот же тип данные).

Что касается typeof: во-первых, typeof является расширением GCC. Это не стандартная часть C. Обычно она используется для написания макросов, которые только оценивают свои аргументы один раз, например (из Руководство GCC):

 #define max(a,b) \
   ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
     _a > _b ? _a : _b; })

Ключевое слово typeof позволяет макросу определять локальное временное значение для сохранения значений его аргументов, позволяя их оценивать только один раз.

Короче говоря, C не поддерживает перегрузку; вам просто нужно сделать func_a(struct a *) и func_b(struct b *), и вызвать правильный. Кроме того, вы можете создать собственную систему самоанализа:

struct my_header {
  int type;
};

#define TYPE_A 0
#define TYPE_B 1

struct a {
  struct my_header header;
  /* ... */
};

struct b {
  struct my_header header;
  /* ... */
};

void func_a(struct a *p);
void func_b(struct b *p);

void func_switch(struct my_header *head);
#define func(p) func_switch( &(p)->header )

void func_switch(struct my_header *head) {
  switch (head->type) {
    case TYPE_A: func_a((struct a *)head); break;
    case TYPE_B: func_b((struct b *)head); break;
    default: assert( ("UNREACHABLE", 0) );
  }
}

Вы должны, конечно, не забывать правильно инициализировать заголовок при создании этих объектов.

Ответ 3

Как уже говорили другие, это не поддерживается на языке C. Однако вы можете проверить размер переменной с помощью функции sizeof(). Это может помочь вам определить, могут ли две переменные хранить один и тот же тип данных.

Прежде чем вы это сделаете, прочитайте комментарии ниже.

Ответ 4

Как уже упоминалось, вы не можете извлечь тип переменной во время выполнения. Однако вы можете создать свой собственный "объект" и сохранить тип вместе с ним. Затем вы сможете проверить его во время выполнения:

typedef struct {
   int  type;     // or this could be an enumeration
   union {
      double d;
      int i;
   } u;
} CheesyObject;

Затем установите тип по мере необходимости в коде:

CheesyObject o;
o.type = 1;  // or better as some define, enum value...
o.u.d = 3.14159;

Ответ 5

Gnu GCC имеет встроенную функцию для сравнения типов __builtin_types_compatible_p.

https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Other-Builtins.html

Эта встроенная функция возвращает 1, если неквалифицированные версии типы type1 и type2 (которые являются типами, а не выражениями) совместимо, 0 в противном случае. Результатом этой встроенной функции может быть используется в целых константных выражениях.

Эта встроенная функция игнорирует квалификаторы верхнего уровня (например, const, летучий). Например, int эквивалентно const int.

Используется в вашем примере:

double doubleVar;
if(__builtin_types_compatible_p(typeof(doubleVar), double)) {
    printf("doubleVar is of type double!");
}

Ответ 6

Это безумно глупо, но если вы используете код:

fprintf("%x", variable)

и вы используете флаг -Wall во время компиляции, тогда gcc выдает предупреждение о том, что он ожидает аргумент "unsigned int", в то время как аргумент имеет тип "____". (Если это предупреждение не появляется, то ваша переменная имеет тип "unsigned int".)

Удачи!

Изменить: Как было показано ниже, это относится только к времени компиляции. Очень полезно при попытке выяснить, почему ваши указатели не ведут себя, но не очень полезны, если это необходимо во время выполнения.

Ответ 7

C - статически типизированный язык. Вы не можете объявить функцию, которая работает с типом A или типом B, и вы не можете объявить переменную, которая имеет тип A или тип B. Каждая переменная имеет явно объявленный и неизменяемый тип, и вы должны использовать это знание.

И когда вы хотите знать, указывает ли void * на представление памяти float или integer - вы должны хранить эту информацию где-то в другом месте. Язык специально разработан, чтобы не заботиться о том, что char * указывает на то, что хранится как int или char.