wake-up-neo.net

Wie überprüfe ich, ob eine Variable in C einen bestimmten Typ hat (vergleiche zwei Typen)?

Wie überprüfe ich in C (nicht C++/C #), ob eine Variable einen bestimmten Typ hat?

Zum Beispiel so etwas:

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

Oder allgemeiner: Wie vergleiche ich zwei Typen, damit compare(double1,double2) den Wert true ergibt und compare(int,double) den Wert false ergibt. Ich möchte auch Strukturen verschiedener Zusammensetzung vergleichen.

Grundsätzlich habe ich eine Funktion, die mit Variablen vom Typ "struct a" und "struct b" arbeitet. Ich möchte eine Sache mit den Variablen "struct a" und die andere mit den Variablen "struct b" machen. Da C keine Überladung unterstützt und der void-Zeiger seine Typinformationen verliert, muss ich den Typ überprüfen. Übrigens, was wäre der Sinn, einen typeof-Operator zu haben, wenn Sie Typen nicht vergleichen können?


Die Sizeof-Methode scheint für mich eine praktische Lösung zu sein. Danke für Ihre Hilfe. Ich finde es immer noch ein bisschen seltsam, da die Typen zur Kompilierzeit bekannt sind, aber wenn ich mir die Prozesse in der Maschine vorstelle, kann ich sehen, warum die Informationen nicht in Typen, sondern in Form von Bytes gespeichert werden. Neben den Adressen ist nur die Größe relevant.

42
con-f-use

Der Typ einer Variablen ist ab sofort in C11 mit der generischen Auswahl _Generic möglich. Es funktioniert zur Kompilierzeit.

Die Syntax ist ein bisschen wie switch. Hier ist ein Beispiel (aus dieser Antwort ):

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

Um es tatsächlich für die manuelle Typprüfung zur Kompilierzeit zu verwenden, können Sie eine enum mit allen erwarteten Typen definieren, etwa wie folgt:

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

Verwenden Sie dann _Generic, um Typen dieser enum zuzuordnen:

#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 unterstützt diese Form der Typüberprüfung nicht. Was Sie fragen, ist in C nicht möglich (zumindest ohne compilerspezifische Erweiterungen; dies wäre jedoch in C++ möglich).

Im Allgemeinen wird mit C erwartet, dass Sie die Typen Ihrer Variablen kennen. Da jede Funktion konkrete Typen für ihre Parameter hat (mit Ausnahme von varargs, nehme ich an), müssen Sie den Funktionskörper nicht überprüfen. Der einzige verbleibende Fall, den ich sehen kann, ist in einem Makrokörper, und C-Makros sind wirklich nicht so mächtig.

Beachten Sie außerdem, dass C keine Typinformationen für die Laufzeit speichert. Das heißt, selbst wenn es hypothetisch eine Typenvergleichserweiterung gab, würde sie nur dann ordnungsgemäß funktionieren, wenn die Typen zur Kompilierzeit bekannt sind (dh es würde nicht funktionieren, zu testen, ob zwei void * auf denselben Datentyp zeigen) .

Was typeof betrifft: Erstens ist typeof eine GCC-Erweiterung. Es ist kein Standard-Teil von C. Es wird normalerweise verwendet, um Makros zu schreiben, die ihre Argumente nur einmal auswerten, z. B. (aus dem GCC-Handbuch ):

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

Mit dem Schlüsselwort typeof kann das Makro ein lokales temporäres Element definieren, um die Werte seiner Argumente zu speichern, sodass sie nur einmal ausgewertet werden können.

Kurz gesagt, C unterstützt keine Überladung. Sie müssen lediglich eine func_a(struct a *) und func_b(struct b *) erstellen und die richtige aufrufen. Alternativ können Sie Ihr eigenes Introspektionssystem erstellen:

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

Sie müssen natürlich daran denken, den Header richtig zu initialisieren, wenn Sie diese Objekte erstellen.

15
bdonlan

Wie schon andere Leute gesagt haben, wird dies in der C-Sprache nicht unterstützt. Sie können jedoch die Größe einer Variablen mit der Funktion sizeof() überprüfen. Auf diese Weise können Sie feststellen, ob zwei Variablen denselben Datentyp speichern können.

Bevor Sie das tun, lesen Sie die Kommentare unten .

10
Steve Walsh

Wie bereits erwähnt, können Sie den Typ einer Variablen nicht zur Laufzeit extrahieren. Sie können jedoch auch Ihr eigenes "Objekt" erstellen und den Typ zusammen mit ihm speichern. Dann könnten Sie es zur Laufzeit überprüfen:

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

Stellen Sie dann den Typ wie gewünscht im Code ein:

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

Gnu GCC verfügt über eine integrierte Funktion zum Vergleichen von Typen __builtin_types_compatible_p.

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

Diese eingebaute Funktion gibt 1 zurück, wenn die nicht qualifizierten Versionen von Die Typen type1 und type2 (dies sind Typen, keine Ausdrücke) sind kompatibel, sonst 0. Das Ergebnis dieser integrierten Funktion kann .__ sein. Wird in ganzzahligen konstanten Ausdrücken verwendet.

Diese integrierte Funktion ignoriert Qualifikationsmerkmale der obersten Ebene (z. B. const, Volatile). Zum Beispiel ist int äquivalent zu const int.

In Ihrem Beispiel verwendet:

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

Das ist verrückt dumm, aber wenn Sie den Code verwenden:

fprintf("%x", variable)

wenn Sie das -Wall-Flag während des Kompilierens verwenden, wird eine Warnung ausgegeben, dass gcc ein Argument von "unsigned int" erwartet, während das Argument vom Typ "____" ist. (Wenn diese Warnung nicht angezeigt wird, hat Ihre Variable den Typ 'unsigned int'.)

Viel Glück!

Edit: Wie unten erwähnt, gilt dies nur für die Kompilierzeit. Sehr hilfreich, wenn Sie herausfinden wollen, warum Ihre Zeiger sich nicht verhalten, aber nicht sehr nützlich, wenn sie zur Laufzeit benötigt werden.

2
Daniel Peirano

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

Hier finden Sie Erklärungen, welche Anweisungen vom Standard und von den Erweiterungen GNU des obigen Codes verwendet werden.

(Vielleicht etwas nicht im Rahmen der Frage, da es sich bei der Frage nicht um Fehler bei Typenkonflikten handelt, aber trotzdem, hier belassen).

1
Green Tree

Als weitere Antwort können Sie dies nun in C11 mit _Generic tun.

Hier ein Beispiel: Ein Makro prüft, ob eine Eingabe mit einem anderen Typ kompatibel ist:

#include <stdbool.h>
#define isCompatible(x, type) _Generic(x, type: true, default: false)

Sie können das Makro wie folgt verwenden:

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
}

Dies kann auch für andere Typen einschließlich Strukturen verwendet werden. Z.B.

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

Und auf Typedefs.

typedef char* string;

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

Sie erhalten jedoch nicht immer die Antwort, die Sie erwarten. Beispielsweise kann es nicht zwischen einem Array und einem Zeiger unterscheiden.

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

Antwort von hier geliehen: http://www.robertgamble.net/2012/01/c11-generic-selections.html

0
Saeed Baig

C ist eine statisch typisierte Sprache. Sie können keine Funktion deklarieren, die für Typ A oder Typ B arbeitet, und Sie können keine Variablen deklarieren, die Typ A oder Typ B enthalten. Jede Variable hat einen explizit deklarierten und nicht veränderbaren Typ, und Sie sollten dieses Wissen verwenden.

Und wenn Sie wissen möchten, ob void * auf die Speicherdarstellung von Gleitkommazahlen oder Ganzzahlen verweist, müssen Sie diese Informationen an anderer Stelle speichern. Die Sprache ist speziell darauf ausgelegt, dass es egal ist, ob char * auf etwas verweist, das als int oder char gespeichert ist.

0
blaze

Zu diesem Zweck habe ich ein einfaches C-Programm dafür geschrieben ....... Es ist in github ... GitHub Link

So funktioniert es ....__: Wandle zuerst dein Double in eine Zeichenfolge namens s .. um.

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

Verwenden Sie dann meine dtype-Funktion, um den Typ zu bestimmen ... Meine Funktion gibt ein einzelnes Zeichen zurück ... Sie können es so verwenden ...

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

Dann können Sie den Vergleich dazu verwenden, um es zu überprüfen ...

0
Subham Debnath