¿Cómo puedo verificar si una variable es de cierto tipo (comparar dos tipos) en C?

En C (no C ++ / C #) ¿cómo puedo verificar si una variable es de cierto tipo?

Por ejemplo, algo como esto:

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

O más general: ¿Cómo comparo dos tipos para que compare(double1,double2) se evalúe como verdadero y compare(int,double) se evalúe como falso? También me gustaría comparar estructuras de diferentes composiciones también.

Básicamente, tengo una función que opera en variables del tipo “struct a” y “struct b”. Quiero hacer una cosa con las variables “struct a” y la otra con las variables “struct b”. Como C no admite sobrecarga y el puntero de void pierde su tipo de información, necesito verificar el tipo. Por cierto, ¿qué sentido tendría tener un operador de typeof , si no puede comparar tipos?


El método sizeof parece ser una solución práctica alternativa para mí. Gracias por tu ayuda. Todavía me resulta un poco extraño ya que los tipos se conocen en tiempo de comstackción, pero si imagino los procesos en la máquina puedo ver, por qué la información no se almacena en términos de tipos, sino en términos de tamaño de bytes. El tamaño es lo único realmente relevante además de las direcciones.

Obtener el tipo de una variable es, a partir de ahora, posible en C11 con la selección _Generic genérico. Funciona en tiempo de comstackción.

La syntax es un poco como el switch . Aquí hay una muestra (de esta respuesta ):

 #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") 

Para usarlo realmente para la verificación manual de tipos de tiempo de comstackción, puede definir una enum con todos los tipos que espera, algo como esto:

 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 }; 

Y luego use _Generic para _Generic tipos a esta 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 no es compatible con esta forma de tipo de introspección. Lo que estás preguntando no es posible en C (al menos sin extensiones específicas del comstackdor, pero sería posible en C ++).

En general, con C se espera que conozcas los tipos de tu variable. Como cada función tiene tipos concretos para sus parámetros (supongo que varargs), no es necesario verificar el cuerpo de la función. El único caso restante que puedo ver está en un macro cuerpo, y, bueno, las macros C no son realmente tan poderosas.

Además, tenga en cuenta que C no retiene ningún tipo de información en tiempo de ejecución. Esto significa que, incluso si, hipotéticamente, hubiera una extensión de comparación de tipo, solo funcionaría correctamente cuando los tipos se conocieran en tiempo de comstackción (es decir, no funcionaría probar si dos void * apuntan al mismo tipo de datos) )

En cuanto a typeof : Primero, typeof es una extensión de GCC. No es una parte estándar de C. Normalmente se usa para escribir macros que solo evalúan sus argumentos una vez, p. Ej. (Del manual de GCC ):

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

La typeof clave typeof permite que la macro defina un temporal local para guardar los valores de sus argumentos, lo que les permite ser evaluados solo una vez.

En resumen, C no admite la sobrecarga; solo tendrá que hacer un func_a(struct a *) y func_b(struct b *) y llamar al correcto. Alternativamente, puedes hacer tu propio sistema de introspección:

 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) ); } } 

Por supuesto, debe recordar inicializar correctamente el encabezado al crear estos objetos.

Como otras personas ya han dicho, esto no es compatible con el lenguaje C. Sin embargo, podría verificar el tamaño de una variable usando la función sizeof() . Esto puede ayudarlo a determinar si dos variables pueden almacenar el mismo tipo de datos.

Antes de hacerlo, lea los comentarios a continuación .

Como han mencionado otros, no puede extraer el tipo de una variable en tiempo de ejecución. Sin embargo, podría construir su propio “objeto” y almacenar el tipo junto con él. Entonces usted podría verificarlo en tiempo de ejecución:

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

A continuación, configure el tipo según sea necesario en el código:

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

Gnu GCC tiene una función integrada para comparar tipos __builtin_types_compatible_p .

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

Esta función integrada devuelve 1 si las versiones no calificadas de los tipos type1 y type2 (que son tipos, no expresiones) son compatibles, 0 en caso contrario. El resultado de esta función incorporada se puede usar en expresiones constantes enteras.

Esta función incorporada ignora los calificadores de nivel superior (por ejemplo, const, volátil). Por ejemplo, int es equivalente a const int.

Usado en tu ejemplo:

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

Esto es increíblemente estúpido, pero si usas el código:

 fprintf("%x", variable) 

y usa la bandera de “Wall” mientras comstack, entonces gcc lanzará una advertencia de que espera un argumento de ‘unsigned int’ mientras el argumento es de tipo ‘____’. (Si esta advertencia no aparece, entonces su variable es de tipo ‘unsigned int’).

¡La mejor de las suertes!

Editar: Como se mencionó a continuación, esto solo se aplica al tiempo de comstackción. Muy útil cuando intentas descubrir por qué tus punteros no se comportan, pero no son muy útiles si es necesario durante el tiempo de ejecución.

Desde 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; \ }) 

Aquí puede encontrar la explicación de qué declaraciones de estándar y qué extensiones de GNU arriba del código usa.

(Tal vez un poco no en el ámbito de la pregunta, ya que la pregunta no es sobre la falla en el tipo de desajuste, pero de todos modos, dejándolo aquí).

C es un lenguaje estáticamente tipado. No puede declarar una función que opere en tipo A o tipo B, y no puede declarar una variable que contenga tipo A o tipo B. Cada variable tiene un tipo explícitamente declarado e inmutable, y se supone que debe usar este conocimiento.

Y cuando desee saber si void * apunta a la representación de la memoria de float o integer, debe almacenar esta información en otro lugar. El lenguaje está específicamente diseñado para no importar si char * apunta a algo almacenado como int o char .

Para ese propósito, he escrito un sencillo progtwig de C para eso … Está en github … GitHub Link

Aquí cómo funciona … Primero convierte tu doble en una cadena de caracteres llamada s …

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

Luego use mi función dtype para determinar el tipo … Mi función devolverá un solo carácter … Puede usarlo así …

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

Entonces puedes usar la comparación para verificarlo … Eso es todo …

Como se menciona otra respuesta, ahora puede hacer esto en C11 con _Generic .

Por ejemplo, aquí hay una macro que verificará si alguna entrada es compatible con otro tipo:

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

Puedes usar la macro así:

 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 } 

Esto también se puede usar en otros tipos, incluidas las estructuras. P.ej

 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; } 

Y en typedefs.

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

Sin embargo, no siempre te da la respuesta que esperas. Por ejemplo, no puede distinguir entre una matriz y un puntero.

 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"); } 

Respuesta tomada de aquí: http://www.robertgamble.net/2012/01/c11-generic-selections.html