web-dev-qa-db-fra.com

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

En C (pas C++/C #), comment puis-je 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 pour que compare(double1,double2) soit évalué à true et que compare(int,double) soit évalué à false. Aussi, j'aimerais comparer des structures de composition différente.

En gros, j'ai une fonction qui opère sur des 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". Puisque C ne prend pas en charge la surcharge et que le pointeur void perd ses informations de type, je dois vérifier le type. BTW, quel serait l'intérêt d'avoir un opérateur typeof, si vous ne pouvez pas comparer les types?


La méthode sizeof semble être une solution de contournement pratique pour moi. Merci de votre aide. Je trouve toujours cela un peu étrange puisque les types sont connus au moment de la compilation, mais si j’imagine les processus de la machine, je peux voir pourquoi l’information n’est pas stockée en termes de types, mais en termes de taille en octets. La taille est la seule chose qui compte vraiment, à part les adresses.

42
con-f-use

Obtenir le type d'une variable est désormais possible en C11 avec la sélection générique _Generic. Cela fonctionne au moment de la compilation.

La syntaxe est un peu comme 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 lors de la vérification de type manuelle au moment de la compilation, vous pouvez définir un enum avec tous les types que vous attendez, comme ceci:

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 utilisez ensuite _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)
34
Mints97

C ne supporte pas cette forme d'introspection de type. Ce que vous demandez n’est pas possible en C (du moins sans extensions spécifiques au compilateur; ce serait possible en C++, cependant).

En général, avec C, vous êtes censé connaître les types de votre variable. Puisque chaque fonction a des types concrets pour ses paramètres (sauf pour varargs, je suppose), vous n'avez pas besoin de vérifier le corps de la fonction. Le seul cas restant que je peux voir est dans un corps de macro et, bien, les macros C ne sont pas vraiment si puissantes.

De plus, notez que C ne conserve aucune information de type dans le runtime. Cela signifie que, même si, de manière hypothétique, il existait une extension de comparaison de types, elle ne fonctionnerait correctement que lorsque les types sont connus au moment de la compilation (autrement dit, cela ne fonctionnerait pas de tester si deux void * pointent vers le même type de données) .

En ce qui concerne typeof: Premièrement, typeof est une extension de 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, ce qui leur permet d'être évalués une seule fois.

En bref, C ne supporte pas la surcharge; vous devrez simplement créer une func_a(struct a *) et une func_b(struct b *) et appeler la bonne. 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) );
  }
}

Naturellement, vous devez vous rappeler d’initialiser l’en-tête correctement lors de la création de ces objets.

15
bdonlan

Comme d'autres personnes l'ont déjà dit, cela n'est pas supporté dans le langage C. Vous pouvez toutefois 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 .

10
Steve Walsh

Comme d'autres l'ont mentionné, vous ne pouvez pas extraire le type d'une variable au moment de l'exécution. Cependant, vous pouvez construire votre propre "objet" et stocker le type avec. Ensuite, vous pourrez le vérifier au moment de l'exécution:

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

Puis définissez le type comme il convient dans le code:

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

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 du fichier Les types type1 et type2 (qui sont des types et non des expressions) sont compatible, 0 sinon. Le résultat de cette fonction intégrée peut être utilisé dans les expressions constantes entières.

Cette fonction intégrée ignore les qualificatifs de niveau supérieur (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!");
}
4
juppman

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

fprintf("%x", variable)

et que vous utilisez le drapeau -Wall lors de la compilation, alors gcc émettra un avertissement indiquant qu'il attend un argument de 'unsigned int' alors que l'argument est de type '____'. (Si cet avertissement n'apparaît pas, votre variable est du type 'unsigned int'.)

Bonne chance!

Edit: Comme indiqué ci-dessous, cela ne s'applique qu'au temps de 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.

2
Daniel Peirano

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

Ici vous pouvez trouver une explication sur les instructions des extensions standard et GNU ci-dessus utilisées par le code.

(Peut-être un peu en dehors de la question, puisqu'il ne s'agit pas d'un échec en cas de disparité de type, mais de toute façon, la laisser ici).

1
Green Tree

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

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

#include <stdbool.h>
#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
}

Ceci 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* string;

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

Cependant, cela ne vous donne pas toujours la réponse à laquelle vous vous attendez. Par exemple, il ne peut pas distinguer entre 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 à partir d'ici: http://www.robertgamble.net/2012/01/c11-generic-selections.html

0
Saeed Baig

C est un langage statiquement typé. Vous ne pouvez pas déclarer une fonction qui fonctionne sur le type A ou le type B et vous ne pouvez pas déclarer de variable qui contient 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 en mémoire de float ou integer - vous devez stocker cette information ailleurs. Le langage est spécifiquement conçu pour ne pas se soucier de savoir si char * pointe sur quelque chose stocké sous la forme int ou char .

0
blaze

À cette fin, j’ai écrit un programme simple en C pour cela .... C'est dans 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 ceci ...

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 .... C'est tout ...

0
Subham Debnath