Come posso verificare se una variabile è di un certo tipo (confrontare due tipi) in C?

In C (non C ++ / C #) come faccio a verificare se una variabile è di un certo tipo?

Ad esempio, qualcosa di simile:

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

O più generale: come faccio a confrontare due tipi in modo che il compare(double1,double2) valuti a true, e compare(int,double) valuti a false. Mi piacerebbe anche confrontare le strutture di composizione diversa.

Fondamentalmente, ho una funzione che opera su variabili di tipo “struct a” e “struct b”. Voglio fare una cosa con le variabili “struct a” e l’altra con le variabili “struct b”. Poiché C non supporta il sovraccarico e il puntatore del void perde le informazioni sul tipo, è necessario controllare il tipo. A proposito, che senso ha avere un operatore di tipo, se non è ansible confrontare i tipi?


Il metodo sizeof sembra essere una soluzione pratica per me. Grazie per l’aiuto. Lo trovo ancora un po ‘strano dato che i tipi sono noti al momento della compilazione, ma se immagino i processi nella macchina che posso vedere, perché le informazioni non sono memorizzate in termini di tipi, ma piuttosto in termini di dimensioni dei byte. La dimensione è l’unica cosa veramente rilevante oltre agli indirizzi.

Ottenere il tipo di variabile è, al momento, ansible in C11 con la selezione generica _Generic . Funziona in fase di compilazione.

La syntax è un po ‘come switch . Ecco un esempio (da questa risposta ):

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

Per usarlo effettivamente per il controllo manuale del tipo in fase di compilazione, puoi definire un enum con tutti i tipi che ti aspetti, qualcosa del genere:

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

E poi usa _Generic per abbinare i tipi a questo 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 non supporta questa forma di tipo introspezione. Quello che stai chiedendo non è ansible in C (almeno senza estensioni specifiche del compilatore, ma sarebbe comunque ansible in C ++).

In generale, con C ci si aspetta che tu conosca i tipi della tua variabile. Dal momento che ogni funzione ha tipi concreti per i suoi parametri (ad eccezione di varargs, suppongo), non è necessario controllare il corpo della funzione. L’unico caso rimanente che riesco a vedere si trova in un corpo macro e, beh, i macro C non sono poi così potenti.

Inoltre, si noti che C non conserva alcuna informazione di tipo in runtime. Ciò significa che, anche se, ipoteticamente, esistesse un’estensione di confronto dei tipi, funzionerebbe correttamente solo quando i tipi sono noti in fase di compilazione (cioè, non funzionerebbe per verificare se due punti void * allo stesso tipo di dati ).

Per quanto riguarda typeof : Primo, typeof è un’estensione GCC. Non è una parte standard di C. In genere viene utilizzato per scrivere macro che valutano solo i loro argomenti una volta, ad esempio (dal manuale GCC ):

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

La parola chiave typeof consente alla macro di definire un temporaneo locale per salvare i valori dei suoi argomenti, consentendo di valutarli una sola volta.

In breve, C non supporta il sovraccarico; dovrete semplicemente fare un func_a(struct a *) e func_b(struct b *) , e chiamare quello corretto. In alternativa, puoi creare il tuo sistema di introspezione:

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

Ovviamente, è necessario ricordare di inizializzare correttamente l’intestazione durante la creazione di questi oggetti.

Come altre persone hanno già detto questo non è supportato nel linguaggio C. È comunque ansible controllare la dimensione di una variabile usando la funzione sizeof() . Questo può aiutare a determinare se due variabili possono memorizzare lo stesso tipo di dati.

Prima di farlo, leggi i commenti qui sotto .

Come altri hanno già detto, non è ansible estrarre il tipo di una variabile in fase di runtime. Tuttavia, è ansible creare il proprio “object” e memorizzare il tipo insieme ad esso. Quindi sarai in grado di controllarlo in fase di runtime:

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

Quindi imposta il tipo in base alle esigenze nel codice:

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

Gnu GCC ha una funzione incorporata per il confronto dei tipi __builtin_types_compatible_p .

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

Questa funzione built-in restituisce 1 se le versioni non qualificate dei tipi type1 e type2 (che sono tipi, non espressioni) sono compatibili, 0 altrimenti. Il risultato di questa funzione built-in può essere utilizzato nelle espressioni costanti intere.

Questa funzione incorporata ignora i qualificatori di primo livello (ad esempio, const, volatile). Ad esempio, int è equivalente a const int.

Utilizzato nel tuo esempio:

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

Questo è follemente stupido, ma se usi il codice:

 fprintf("%x", variable) 

e si usa il flag -Wall durante la compilazione, quindi gcc darà un avvertimento che si aspetta un argomento di ‘unsigned int’ mentre l’argomento è di tipo ‘____’. (Se questo avviso non appare, allora la tua variabile è di tipo ‘unsigned int’).

Buona fortuna!

Modifica: come è stato mostrato qui sotto, questo si applica solo al tempo di compilazione. Molto utile quando si cerca di capire perché i propri indicatori non si comportano, ma non è molto utile se necessario durante il tempo di esecuzione.

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

Qui puoi trovare spiegazioni quali dichiarazioni da standard e quali estensioni GNU sopra il codice usa.

(Forse un po ‘non nel campo di applicazione della domanda, dal momento che la domanda non riguarda il fallimento nella mancata corrispondenza di tipo, ma comunque, lasciandolo qui).

C è un linguaggio tipizzato staticamente. Non è ansible dichiarare una funzione che funziona su tipo A o tipo B, e non è ansible dichiarare una variabile che contiene il tipo A o il tipo B. Ogni variabile ha un tipo esplicitamente dichiarato e non modificabile e si suppone di utilizzare questa conoscenza.

E quando vuoi sapere se void * punta alla rappresentazione della memoria di float o intero – devi memorizzare queste informazioni da qualche altra parte. La lingua è specificatamente progettata per non preoccuparsi se char * punta a qualcosa memorizzato come int o char .

A tale scopo ho scritto un semplice programma in C per questo … È in github … GitHub Link

Ecco come funziona … Prima converti il ​​tuo doppio in una stringa di caratteri chiamata s ..

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

Quindi usa la mia funzione dtype per determinare il tipo … La mia funzione restituirà un singolo carattere … Puoi usarlo in questo modo …

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

Quindi puoi usare il confronto per controllarlo … È così …

Come un’altra risposta menzionata, ora puoi farlo in C11 con _Generic .

Ad esempio, ecco una macro che verificherà se alcuni input sono compatibili con un altro tipo:

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

Puoi usare la macro in questo modo:

 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 } 

Questo può essere utilizzato anche su altri tipi, incluse le strutture. Per esempio

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

E su typedef.

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

Tuttavia, non sempre ti dà la risposta che ti aspetti. Ad esempio, non è in grado di distinguere tra una matrice e un puntatore.

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

Risposta presa in prestito da qui: http://www.robertgamble.net/2012/01/c11-generic-selections.html