Wie überprüfe ich, ob eine Variable in C von einem bestimmten Typ ist (zwei Typen vergleichen)?

Lesezeit: 9 Minuten

Benutzeravatar von con-f-use
verwechseln

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) wird als wahr ausgewertet, und compare(int,double) wird als falsch ausgewertet. Außerdem möchte ich auch Strukturen unterschiedlicher 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 “struct a”-Variablen und die andere mit den “struct b”-Variablen machen. Da C kein Überladen unterstützt und die void Zeiger verliert seine Typinformationen, die ich auf Typ überprüfen muss. Übrigens, was wäre der Sinn, wenn man eine typeof Betreiber, wenn Sie Typen nicht vergleichen können?


Die sizeof-Methode scheint für mich eine praktische Workaround-Lösung zu sein. Danke für Ihre Hilfe. Ich finde es immer noch etwas 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 Form von Typen, sondern in Form von Bytegröße gespeichert werden. Neben Adressen ist eigentlich nur die Größe relevant.

  • Kannst du nicht beide zu einem Double werfen (und addieren 0.00)? Nicht sicher, ob dies in C möglich ist, nur ein Vorschlag.

    – Kevin

    8. Juni 2011 um 14:13 Uhr


  • Schauen Sie in den Quellcode, dort steht, dass doubleVar ein Double ist. Keine Notwendigkeit (und auch nicht möglich), es zur Laufzeit zu überprüfen.

    – Habalusa

    8. Juni 2011 um 14:13 Uhr


  • Als Antwort auf Edit #1: Haben Sie darüber nachgedacht, Funktionszeiger (wie eine vtable) zu verwenden, um Ihr Problem zu lösen?

    – Michael Foukarakis

    8. Juni 2011 um 16:07 Uhr

  • Wenn Ihnen die sizeof-Methode gefällt, lesen Sie dieser Artikel über die tgmath-Implementierung von gcc.

    – Quinmare

    8. Juni 2011 um 21:01 Uhr

  • @Michael Foukarakis Würdest du ein Beispiel nennen?

    – con-f-Gebrauch

    8. Juni 2011 um 21:53 Uhr

Den Typ einer Variablen zu erhalten ist ab sofort in C11 mit dem möglich _Generic generische Auswahl. Es funktioniert zur Kompilierzeit.

Die Syntax ist ein bisschen so für 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 Typüberprüfung während der Kompilierung zu verwenden, können Sie eine definieren enum mit allen Typen, die Sie erwarten, etwa so:

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

Und dann verwenden _Generic Typen darauf abzustimmen 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)

Benutzeravatar von bdonlan
bdonlan

C unterstützt diese Form der Typenintrospektion nicht. Was Sie fragen, ist in C nicht möglich (zumindest ohne Compiler-spezifische Erweiterungen; in C++ wäre es jedoch möglich).

Im Allgemeinen wird bei 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 einchecken. Der einzige verbleibende Fall, den ich sehen kann, ist in einem Makrokörper, und C-Makros sind nicht wirklich allzu leistungsfähig.

Beachten Sie außerdem, dass C keine Typinformationen zur Laufzeit behält. Das bedeutet, dass, selbst wenn es hypothetisch eine Typvergleichserweiterung gäbe, diese nur dann richtig funktionieren würde, wenn die Typen zur Kompilierzeit bekannt sind (d. h. es würde nicht funktionieren, zu testen, ob zwei void * auf den gleichen Datentyp verweisen).

Wie für typeof: Zuerst, typeof ist eine GCC-Erweiterung. Es ist kein Standardbestandteil von C. Es wird normalerweise verwendet, um Makros zu schreiben, die ihre Argumente nur einmal auswerten, z GCC-Handbuch):

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

Das typeof Mit dem Schlüsselwort 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 kein Überladen; Sie müssen nur eine machen func_a(struct a *) und func_b(struct b *), und rufen Sie die richtige an. Alternativ könnten 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) );
  }
}

Beim Erstellen dieser Objekte müssen Sie natürlich daran denken, den Header richtig zu initialisieren.

  • Nicht einmal ein Workaround oder cleverer Trick mit Makros oder so?

    – con-f-Gebrauch

    8. Juni 2011 um 14:17 Uhr

  • @con-f-use, warum brauchst du das?

    – bdonlan

    8. Juni 2011 um 14:17 Uhr

  • @con-f-use Makros arbeiten zur Kompilierzeit. Sie wissen genauso viel wie Sie, wenn Sie den Code schreiben.

    – Cnicutar

    8. Juni 2011 um 14:18 Uhr

  • @cnicutar Ich weiß, was ein Makro ist. Vielen Dank! @bdonlan Ich fühle mich fast schlecht, wenn ich deine Antwort nicht akzeptiere. Die sizeof-Idee war einfach ein besserer Workaround für mich. +10 für dich mein Freund und vielen Dank.

    – con-f-Gebrauch

    8. Juni 2011 um 14:36 ​​Uhr

  • @con-f-use, es ist eine großartige Problemumgehung, bis Sie ein Mitglied hinzufügen, die Größen gleich werden und plötzlich immer die struct a Zweig, auch wenn es ein ist struct b. 🙂

    – bdonlan

    8. Juni 2011 um 14:38 Uhr

Benutzeravatar von Steve Walsh
Steve Walsch

Wie andere Leute bereits gesagt haben, wird dies in der Sprache C nicht unterstützt. Sie können jedoch die Größe einer Variablen mit dem überprüfen sizeof() Funktion. Dies kann Ihnen dabei helfen festzustellen, ob zwei Variablen den gleichen Datentyp speichern können.

Bevor Sie das tun, Lesen Sie die Kommentare unten.

  • Wenn Sie darauf bestehen, fügen Sie außerdem eine statische Behauptung hinzu, um sicherzustellen, dass die Größen niemals versehentlich gleich werden: struct STATIC_ASSERT_size_not_equal_s { char STATIC_ASSERT_size_not_equal[sizeof(a) == sizeof(b) ? -1 : 1]; };

    – bdonlan

    8. Juni 2011 um 14:36 ​​Uhr

  • In meinem Fall des Vergleichs von Strukturen haben beide die gleichen Mitglieder, mit Ausnahme von einem mit zwei zusätzlichen Doppelmitgliedern. Ich sollte also sicher sein, wenn ich “if (sizeof (a)> sizeof (b))” unabhängig von der Architektur oder anderen Dingen mache. Trotzdem danke.

    – con-f-Gebrauch

    8. Juni 2011 um 14:47 Uhr


  • “Sie könnten jedoch die Größe einer Variablen mit der Funktion sizeof() überprüfen” sizeof (int) == sizeof (float) aber sie haben ein völlig anderes Speicherformat.

    – phoxie

    8. Juni 2011 um 15:26 Uhr

Gnu GCC hat eine eingebaute Funktion zum Vergleichen von Typen __builtin_types_compatible_p.

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

Diese integrierte Funktion gibt 1 zurück, wenn die nicht qualifizierten Versionen der Typen Typ1 und Typ2 (die Typen und keine Ausdrücke sind) kompatibel sind, andernfalls 0. Das Ergebnis dieser integrierten Funktion kann in ganzzahligen Konstantenausdrücken verwendet werden.

Diese eingebaute Funktion ignoriert Kennzeichner der obersten Ebene (z. B. const, volatile). Beispielsweise ist int äquivalent zu const int.

In deinem Beispiel verwendet:

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

Wie andere bereits erwähnt haben, können Sie den Typ einer Variablen zur Laufzeit nicht extrahieren. Sie könnten jedoch Ihr eigenes “Objekt” konstruieren 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;

Legen Sie dann den Typ nach Bedarf im Code fest:

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

Aus 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 eine Erklärung, welche Anweisungen aus dem Standard und welche GNU-Erweiterungen der obige Code verwendet.

(Vielleicht ein bisschen nicht im Umfang der Frage, da es bei der Frage nicht um Fehler bei Typkonflikten geht, aber trotzdem hier bleiben).

Benutzeravatar von Saeed Baig
Said Baig

Wie bereits in einer anderen Antwort erwähnt, können Sie dies jetzt in C11 mit tun _Generic.

Hier ist zum Beispiel ein Makro, das 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 verwendet werden, einschließlich Strukturen. 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");
}

Allerdings erhalten Sie 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 ausgeliehen: http://www.robertgamble.net/2012/01/c11-generic-selections.html

1420980cookie-checkWie überprüfe ich, ob eine Variable in C von einem bestimmten Typ ist (zwei Typen vergleichen)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy