3 Stimmen

Gibt es einen Weg, einen Strukturtyp an eine c-Funktion zu übergeben?

Ich habe einige Code mit mehreren Funktionen, die sich sehr ähnlich sind, um ein Element in einer Liste basierend auf dem Inhalt eines Feldes in einer Struktur zu suchen. Der einzige Unterschied zwischen den Funktionen besteht darin, dass der Typ der Struktur, in dem die Suche stattfindet, unterschiedlich ist. Wenn ich den Typ übergeben könnte, könnte ich die Code-Duplizierung entfernen.

Ich habe auch bemerkt, dass in diesen Funktionen einige Mutex-Sperren stattfinden, daher denke ich, dass ich sie lieber in Ruhe lasse...

6voto

tialaramex Punkte 3680

Wenn Sie sicherstellen, dass das Feld an derselben Stelle in jeder solchen Struktur platziert ist, können Sie einfach einen Zeiger umwandeln, um auf das Feld zuzugreifen. Diese Technik wird in vielen Low-Level-Systembibliotheken verwendet, z. B. bei BSD-Sockets.

struct person {
  int index;
};

struct clown {
  int index;
  char *hat;
};

/* wir werden hier keinen Feuerwehrwagen definieren */
struct firetruck;

struct fireman {
  int index;
  struct firetruck *truck;
};

int getindexof(struct person *who)
{
  return who->index;
}

int main(int argc, char *argv[])
{
  struct fireman sam;
  /* irgendwie wird sam initialisiert */
  sam.index = 5;

  int index = getindexof((struct person *) &sam);
  printf("Sam's index is %d\n", index);

  return 0;
}

Durch dies verlieren Sie die Typsicherheit, aber es ist eine wertvolle Technik.

[ Ich habe den obigen Code jetzt tatsächlich getestet und die verschiedenen kleinen Fehler behoben. Es ist viel einfacher, wenn Sie einen Compiler haben. ]

3voto

foxxtrot Punkte 10954

Da Strukturen nichts anderes als vordefinierte Speicherblöcke sind, können Sie dies tun. Sie könnten ein void * an die Struktur übergeben und einen Integer oder etwas ähnliches, um den Typ zu definieren.

Von dort aus wäre das sicherste, das void * in einen Zeiger des entsprechenden Typs umzuwandeln, bevor Sie auf die Daten zugreifen.

Sie müssen sehr, sehr vorsichtig sein, da Sie die Typsicherheit verlieren, wenn Sie zu einem void * umwandeln und wahrscheinlich bei etwas wie diesem einen schwer zu debuggenden Laufzeitfehler erhalten.

1voto

Jonathan Leffler Punkte 694013

Ich denke, du solltest dir die C-Standardfunktionen qsort() und bsearch() ansehen, um Inspiration zu finden. Diese sind allgemeine Code, um Arrays zu sortieren und nach Daten in einem vorsortierten Array zu suchen. Sie funktionieren mit jedem Datentyp - aber du übergibst ihnen einen Zeiger auf eine Hilfsfunktion, die die Vergleiche durchführt. Die Hilfsfunktion kennt die Details der Struktur und führt daher den Vergleich korrekt durch.

Tatsächlich, da du Suchvorgänge durchführen möchtest, könnte es sein, dass du nur bsearch() benötigst, obwohl du, wenn du die Datenstrukturen während der Laufzeit erstellst, entscheiden könntest, dass du eine andere Struktur als eine sortierte Liste benötigst. (Du kannst sortierte Listen verwenden - es neigt jedoch dazu, die Dinge im Vergleich zu, sagen wir, einem Heap, zu verlangsamen. Du würdest jedoch eine allgemeine heap_search() Funktion und eine heap_insert() Funktion benötigen, um den Job ordnungsgemäß zu erledigen, und solche Funktionen sind nicht standardisiert in C. Eine Websuche zeigt, dass solche Funktionen existieren - nicht unter diesem Namen; versuche es nur nicht mit "c heap search", da angenommen wird, dass du "günstige Suche" gemeint hast und du eine Menge Müll bekommst!)

1voto

fizzer Punkte 13343

Wenn das ID-Feld, das Sie testen, Teil einer gemeinsamen anfänglichen Sequenz von Feldern ist, die von allen structs gemeinsam genutzt werden, garantiert die Verwendung einer Union, dass der Zugriff funktioniert:

#include 

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int id;
    long junk2;
} Bar;

typedef union
{
    struct
    {
        int id;
    } common;

    Foo foo;
    Bar bar;
} U;

int matches(const U *candidate, int wanted)
{
    return candidate->common.id == wanted;
}

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 42, 0 };

    U fu;
    U bu;

    fu.foo = f;
    bu.bar = b;

    puts(matches(&fu, 23) ? "true" : "false");
    puts(matches(&bu, 42) ? "true" : "false");

    return 0;
}

Wenn Sie Pech haben und das Feld an verschiedenen Offsets in den verschiedenen structs erscheint, können Sie einen Offset-Parameter zu Ihrer Funktion hinzufügen. Dann simulieren offsetof und eine Wrapper-Makro, was vom OP verlangt wurde - die Angabe des Strukturtyps an der Aufrufstelle:

#include 
#include 

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int junk2;
    int id;
} Bar;

int matches(const void* candidate, size_t idOffset, int wanted)
{
    return *(int*)((const unsigned char*)candidate + idOffset) == wanted;
}

#define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted)

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 0, 42 };
    puts(MATCHES(Foo, &f, 23) ? "true" : "false");
    puts(MATCHES(Bar, &b, 42) ? "true" : "false");

    return 0;
}

0voto

JohnnyLambada Punkte 12170

Eine Möglichkeit, dies zu tun, ist ein Typfeld als erstes Byte der Struktur zu haben. Ihre Empfangsfunktion betrachtet dieses Byte und wandelt dann den Zeiger in den richtigen Typ um, basierend auf dem, was sie entdeckt. Ein anderer Ansatz ist, die Typinformationen als separaten Parameter an jede Funktion zu übergeben, die sie benötigt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X