Comment vérifier si une variable est d’un certain type (comparer deux types) en C?

En C (pas en C ++ / C #) comment vérifier si une variable est d’un certain type?

Par exemple, quelque chose comme ceci:

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

Ou plus généralement: comment comparer deux types afin que compare(double1,double2) soit évalué à true, et compare(int,double) sera évalué à false. J’aimerais aussi comparer des structures de composition différente.

Fondamentalement, j’ai une fonction qui opère sur les variables de type “struct a” et “struct b”. Je veux faire une chose avec les variables “struct a” et l’autre avec les variables “struct b”. Comme C ne prend pas en charge la surcharge et que le pointeur void perd son information de type, je dois vérifier le type. BTW, quel serait le sens d’avoir un opérateur de typeof , si vous ne pouvez pas comparer les types?


La méthode sizeof semble être une solution pratique pour moi. Merci de votre aide. Je trouve toujours cela un peu étrange car les types sont connus au moment de la compilation, mais si j’imagine les processus dans la machine, je peux voir pourquoi les informations ne sont pas stockées en termes de types, mais en termes de taille en octets. La taille est la seule chose vraiment pertinente en dehors des adresses.

Obtenir le type d’une variable est, à présent, possible dans C11 avec la sélection générique _Generic . Cela fonctionne à la compilation.

La syntaxe est un peu comme la switch . Voici un exemple (de cette réponse ):

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

Pour l’utiliser réellement pour la vérification manuelle de type à la compilation, vous pouvez définir un enum avec tous les types que vous attendez, par exemple:

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

Et puis utilisez _Generic pour faire correspondre les types à cette 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 ne supporte pas cette forme de type introspection. Ce que vous demandez n’est pas possible en C (du moins sans extensions spécifiques au compilateur, mais cela serait possible en C ++).

En général, avec C, vous devez connaître les types de votre variable. Comme chaque fonction a des types concrets pour ses parameters (sauf pour varargs, je suppose), vous n’avez pas besoin de vérifier le corps de la fonction. Le seul cas restant que je puisse voir est dans un corps de macro, et, bien, les macros C ne sont pas vraiment très puissantes.

De plus, notez que C ne conserve aucune information de type dans le runtime. Cela signifie que, même si, hypothétiquement, il y avait une extension de comparaison de type, elle ne fonctionnerait correctement que lorsque les types sont connus à la compilation (c.-à-d. Que cela ne fonctionnerait pas ).

Comme pour typeof : Tout d’abord, typeof est une extension GCC. Ce n’est pas une partie standard de C. Il est généralement utilisé pour écrire des macros qui n’évaluent leurs arguments qu’une seule fois, par exemple (à partir du manuel GCC ):

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

Le mot-clé typeof permet à la macro de définir un temporaire local pour enregistrer les valeurs de ses arguments, leur permettant ainsi d’être évalués une seule fois.

En bref, C ne supporte pas la surcharge; il vous suffira de créer un func_a(struct a *) et un func_b(struct b *) , et d’appeler le bon. Vous pouvez également créer votre propre système d’introspection:

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

Vous devez bien sûr vous rappeler d’initialiser correctement l’en-tête lors de la création de ces objects.

Comme d’autres l’ont déjà dit, cela n’est pas supporté dans le langage C. Vous pouvez cependant vérifier la taille d’une variable à l’aide de la fonction sizeof() . Cela peut vous aider à déterminer si deux variables peuvent stocker le même type de données.

Avant de faire cela, lisez les commentaires ci-dessous .

Comme d’autres l’ont mentionné, vous ne pouvez pas extraire le type d’une variable à l’exécution. Cependant, vous pouvez construire votre propre “object” et stocker le type avec lui. Vous pourrez alors le vérifier à l’exécution:

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

Ensuite, définissez le type requirejs dans le code:

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

Gnu GCC a une fonction intégrée pour comparer les types __builtin_types_compatible_p .

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

Cette fonction intégrée renvoie 1 si les versions non qualifiées des types type1 et type2 (qui sont des types, pas des expressions) sont compatibles, 0 sinon. Le résultat de cette fonction intégrée peut être utilisé dans des expressions constantes de type entier.

Cette fonction intégrée ignore les qualificatifs de premier niveau (par exemple, const, volatile). Par exemple, int est équivalent à const int.

Utilisé dans votre exemple:

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

C’est follement stupide, mais si vous utilisez le code:

 fprintf("%x", variable) 

et si vous utilisez le drapeau -Wall lors de la compilation, alors gcc lancera un avertissement indiquant qu’il attend un argument de ‘unsigned int’ alors que l’argument est de type ‘____’. (Si cet avertissement ne s’affiche pas, votre variable est de type ‘unsigned int’.)

Bonne chance!

Edit: Comme indiqué ci-dessous, cela ne s’applique qu’à la compilation. Très utile lorsque vous essayez de comprendre pourquoi vos pointeurs ne se comportent pas, mais pas très utile si nécessaire pendant l’exécution.

De linux / typecheck.h :

 /* * Check at comstack 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; \ }) 

Vous trouverez ici des explications sur les instructions standard et les extensions GNU ci-dessus utilisées.

(Peut-être un peu pas dans la scope de la question, puisque la question ne concerne pas l’échec sur l’inadéquation de type, mais de toute façon, le laisser ici).

C est un langage typé statiquement. Vous ne pouvez pas déclarer une fonction qui opère sur le type A ou le type B, et vous ne pouvez pas déclarer une variable contenant le type A ou le type B. Chaque variable a un type explicitement déclaré et non modifiable, et vous êtes censé utiliser cette connaissance.

Et lorsque vous voulez savoir si void * pointe vers la représentation mémoire de float ou integer – vous devez stocker cette information ailleurs. Le langage est spécifiquement conçu pour ne pas se soucier si char * pointe vers quelque chose stocké comme int ou char .

Pour cela j’ai écrit un simple programme en C pour ça … C’est en github … GitHub Link

Voici comment cela fonctionne … Convertissez d’abord votre double en une chaîne de caractères nommée s ..

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

Puis utilisez ma fonction dtype pour déterminer le type … Ma fonction renverra un seul caractère … Vous pouvez l’utiliser comme ça …

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

Ensuite, vous pouvez utiliser la comparaison pour le vérifier … Ça y est …

Comme une autre réponse mentionnée, vous pouvez maintenant le faire en C11 avec _Generic .

Par exemple, voici une macro qui vérifiera si une entrée est compatible avec un autre type:

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

Vous pouvez utiliser la macro comme suit:

 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 } 

Cela peut également être utilisé sur d’autres types, y compris les structures. Par exemple

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

Et sur les typedefs.

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

Cependant, il ne vous donne pas toujours la réponse que vous attendez. Par exemple, il ne peut pas distinguer un tableau et un pointeur.

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

Réponse empruntée ici: http://www.robertgamble.net/2012/01/c11-generic-selections.html