Hallo, ich arbeite an einer Zuweisung in C, wo ich einen unbekannten Parametertyp in eine Funktion eingeben muss.
Angenommen, ich habe Folgendes:
%Vor%Der Grund, warum variable Elemente ungültig sind, liegt darin, dass es drei Arten von Möglichkeiten gibt. Alle 3 haben jedoch eine Membervariable namens "Count".
Wenn ich versuche, den tatsächlichen Code, den ich in Eclipse geschrieben habe, zu kompilieren, erhalte ich den folgenden Fehler:
Fehler: Anfrage für Mitglied 'Count' in etwas nicht eine Struktur oder eine Vereinigung
Ich vermute, das passiert, weil der Compiler den Typ des "Elements" vorher nicht kennt. Ich sehe jedoch nicht, warum das nicht funktioniert.
Danke für Ihre Hilfe!
Zuerst geben Sie einen Zeiger auf void ein, was ein gültiger Ansatz für unbekannte Typen ist, aber Sie müssen Zeiger auf Ihre verschiedenen Objekttypen übergeben.
C ist keine dynamische Sprache, daher werden symbolische Informationen vor der Laufzeit weitgehend gelöscht. Wenn Sie also sagen, dass Ihre drei Typen alle ein Mitglied Count
haben, hilft das bei der Gestaltung Ihrer Funktion nicht.
Der einzige Weg, auf den Sie Count
zugreifen können, besteht darin, Ihren void*
-Parameter auf den korrekten Zeigertyp zu transformieren, bevor Sie mit ->
oder (*element).Count
(d. h. nicht nur .
) deferencieren.
Sofern Sie nicht darauf angewiesen sind, dass Ihre Typen ein kompatibles Layout haben (das wahrscheinlich von der Implementierung abhängig ist), müssen Sie auch etwas übergeben, das Ihrer Funktion hilft, die richtige Besetzung zu bestimmen. An diesem Punkt sind Sie vielleicht besser dran mit drei separaten Funktionen und besserer Sicherheit.
Sie müssen es in den tatsächlichen Typ umwandeln, aber Sie erhalten möglicherweise unerwartete Ergebnisse in den Strukturen, die Eigenschaft count nicht an der gleichen Stelle.
%Vor%Wenn Sie einen Zeiger an eine Struktur übergeben, die wie folgt aussieht, erhöhen Sie das x-Feld und nicht die Anzahl.
%Vor% Wenn das Element .Count
für jeden dieser drei Typen vom selben Typ ist, können Sie auch ein Makro verwenden. Angenommen, es ist int, können Sie dies tun:
Das wird funktionieren, weil die a.Count
-Adresse beim Aufruf der Funktion aufgelöst wird, nicht danach (wenn Sie den Typ nicht mehr kennen). Ich nehme an, dass Sie beim Aufruf der Funktion den richtigen Typ haben. Also Sometype x; changeCount(x);
wird funktionieren, aber die Weitergabe von etwas, das bereits ein (void*)
ist, wird nicht funktionieren.
Auch dein ursprünglicher Ausdruck element.Count = element.Count++;
ist ziemlich bizarr. Wenn Sie inkrementieren möchten, verwenden Sie element.Count++
oder element.Count = element.Count + 1
.
Beim Kompilieren verwirft C [1] die meisten Informationen und hinterlässt nur Offsets. Also würde Ihre Funktion im Pseudocode zu so etwas kompilieren:
%Vor%Der stack_ptr ist der Speicherort des Stack-Frames, der beim Aufruf von changeCount erstellt wurde, das offset_element ist der Speicherort des element-Arguments relativ zum stack_ptr, aber was ist offset_Count? Denken Sie daran, dass der Compiler über Ihren Code nur weiß, was Sie in Ihrem Beispiel gezeigt haben; Element ist ein generischer Zeiger, nicht wirklich ein Zeiger auf irgendetwas. Sie müssen dem Compiler mitteilen, auf welches Element er verweist, indem Sie ihn einer Variablen [2] übergeben oder zuweisen:
%Vor%Diese Funktion erzeugt im Wesentlichen den gleichen (Pseudo-) Code wie oben, aber der Compiler weiß nun, wie der Offset von Count sein sollte.
Sie erwähnen, dass es drei Möglichkeiten gibt, auf welchen Typ welches Element zeigt. Es gibt eine Reihe von Möglichkeiten, dies zu handhaben: Entweder eine distinguierte Vereinigung oder eine "ererbte" Struktur. Für eine distinguierte Vereinigung verwenden Sie zum Beispiel eine Struktur, bei der ein Element eine Enumeration ist, die angibt, welche der drei Möglichkeiten und ein anderes Element eine Vereinigung der drei möglichen Strukturen ist; Dies ist ungefähr das, was die ML-Sprachen (OCaml, Haskell, etc.) einen algebraischen Datentyp nennen oder was eine Union in Pascal ist. Für "Vererbung" könnten Sie Typdefinitionen verwenden:
%Vor%In diesem Fall könnten Sie die ChangeCount-Funktion oben verwenden und einen Zeiger an eine counted_int_t, counted_double_t oder counted_charptr_t übergeben.
Was passiert, ist, dass der Compiler die drei Strukturen mit dem Count-Element in den "Nachkommen" -Strukturen an der gleichen Stelle auslegt, solange das Element counted_t das erste ist. (Zumindest in jedem Compiler, den ich je benutzt habe und in jedem Code, den ich gesehen habe. Ich glaube, das hat es irgendwann in den C-Standard geschafft, aber es ist ein ganz normales Idiom.)
[1] Mit Ausnahme von Debugging-Informationen, wenn Sie dem Compiler mitgeteilt haben, dass es ausgegeben werden soll. Ihr Programm hat jedoch keinen Zugriff auf diese Informationen, daher würde es in diesem Fall nicht helfen.
[2] Die Operation x ++ (postincrement) inkrementiert die Variable (well, lvalue), auf die sie angewendet wird. Die Zuweisung im ursprünglichen Code ist nicht notwendig.
Das ist genau das Problem:
Ich vermute, das passiert weil der Compiler das nicht kennt Art von "Element" vor der Hand. jedoch Ich verstehe nicht, warum das nicht funktioniert.
Das Aufrufen einer Methode oder von Mitgliedsdaten nach Namen ist im Allgemeinen kein Merkmal statisch typisierter Sprachen.
Sogar ein void *
kann nur zuverlässig in T *
umgewandelt werden, wobei T ein Typ ist, wenn der Zeiger tatsächlich ein Zeiger auf diesen Typ ist.
In C ++ besteht eine Möglichkeit darin, dass alle drei Typen von derselben virtuellen Bass-Klasse X erben, die eine Methode Count (d. h. eine Schnittstelle ICountable) aufweist. Dann wird in X *
umgewandelt und p->Count
verwendet. Dies wäre idiomatische C ++ - alle Klassen implementieren die gleiche Schnittstelle und alle unterstützen diese Methode. Dies wäre eine sprachunterstützte Methode, die analog zu dem in der Antwort von Tommy McGuire gezeigten Struct Offsets-Trick funktioniert, der die Structures nach Konvention ähnlich macht. Wenn Sie die Strukturen ändern oder der Compiler vom Standard für das Auslegen von Strukturen abweichen würde, wären Sie in heißem Wasser.
Ich kann nicht umhin zu denken, dass dies eher ein Spielzeugproblem ist, da die Methode so einfach ist, würde man sie normalerweise nicht in eine Funktion einfügen - man würde es einfach inline nennen: T t; t.Count++;
.
Tags und Links c void-pointers