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

В C (не 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 кажется практическим решением для меня. Спасибо за вашу помощь. Мне все еще кажется, что это немного странно, поскольку типы известны во время компиляции, но если я представляю процессы на машине, которые я вижу, то почему информация не хранится в терминах типов, а скорее по размеру байта. Размер – это единственное, что действительно актуально, кроме адресов.

    Получение типа переменной на данный момент возможно на C11 с _Generic универсального выделения _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) 

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

    В общем, с C вы должны знать типы вашей переменной. Поскольку каждая функция имеет конкретные типы для своих параметров (за исключением varargs, я полагаю), вам не нужно проверять тело функции. Единственный оставшийся случай, который я вижу, находится в макрообъекте, и, ну, macros 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) ); } } 

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

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

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

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

     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... oud = 3.14159; 

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

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

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

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

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

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

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

     fprintf("%x", variable) 

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

    Удачи!

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

    Из linux / typecheck.h :

     /* * Check at compile time that something is of a particular type. * Always evaluates to 1 so you may use it easily in comparisons. */ #define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ }) 

    Здесь вы можете найти объяснение, какие утверждения из стандарта и какие расширения GNU над кодом используются.

    (Может быть, немного не в вопросе вопроса, поскольку вопрос не о неудаче при несоответствии типа, но в любом случае, оставив его здесь).

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

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

    Для этой цели я написал простую программу C для этого … Он находится в github … GitHub Link

    Здесь, как это работает … Сначала преобразуйте свой двойной в строку символов с именем s ..

     char s[50]; sprintf(s,"%.2f", yo); 

    Затем используйте функцию dtype для определения типа … Моя функция вернет один символ … Вы можете использовать его вот так …

     char type=dtype(s); //Return types are : //i for integer //f for float or decimals //c for character... 

    Тогда вы можете использовать сравнение, чтобы проверить это … Вот и все …

    Как уже упоминалось, вы можете сделать это на C11 с помощью _Generic .

    Например, вот макрос, который будет проверять, совместим ли какой-либо вход с другим типом:

     #include  #define isCompatible(x, type) _Generic(x, type: true, default: false) 

    Вы можете использовать макрос следующим образом:

     double doubleVar; if (isCompatible(doubleVar, double)) { printf("doubleVar is of type double!\n"); // prints } int intVar; if (isCompatible(intVar, double)) { printf("intVar is compatible with double too!\n"); // doesn't print } 

    Это также можно использовать для других типов, включая структуры. Например

     struct A { int x; int y; }; struct B { double a; double b; }; int main(void) { struct A AVar = {4, 2}; struct B BVar = {4.2, 5.6}; if (isCompatible(AVar, struct A)) { printf("Works on user-defined types!\n"); // prints } if (isCompatible(BVar, struct A)) { printf("And can differentiate between them too!\n"); // doesn't print } return 0; } 

    И на typedefs.

     typedef char* string; string greeting = "Hello world!"; if (isCompatible(greeting, string)) { printf("Can check typedefs.\n"); } 

    Однако он не всегда дает вам ответ, который вы ожидаете. Например, он не может различать массив и указатель.

     int intArray[] = {4, -9, 42, 3}; if (isCompatible(intArray, int*)) { printf("Treats arrays like pointers.\n"); } // The code below doesn't print, even though you'd think it would if (isCompatible(intArray, int[4])) { printf("But at least this works.\n"); } 

    Ответ заимствован отсюда: http://www.robertgamble.net/2012/01/c11-generic-selections.html

    Interesting Posts

    Как скрыть подсчет ссылок в VS2013?

    Существует ли программное обеспечение для автоматического изменения скорости песни без его переноса

    Сравнение двух массивов объектов и исключение элементов, которые соответствуют значениям в новый массив в JS

    Прокси SSL / Чарльз и проблемы с Android

    использование аргумента переменной длины в scala

    Зачем устанавливать «просто меня», а не «всех»?

    Замена строк в java, аналогично шаблону скорости

    Как strsplit различное количество строк в определенном столбце функцией do

    Обход ограничения размера файла FAT32 4 ГБ

    Где узнать о VS отладчике «волшебные имена»

    Обязательно ли включить платежный аккаунт до 11 июня 2018 года через информацию о кредитной карте, чтобы продолжить доступ к API Карт Google?

    Как включить js и CSS в JSP с помощью Spring MVC

    Инструменты текстового форматирования

    Правильная структура активов SCSS в Rails

    Настройка заголовка авторизации HttpClient

    Давайте будем гением компьютера.