dokumentation Warum unterscheidet C zwischen-> und.?




webedition img (6)

Nun, es ist eindeutig keine Zweideutigkeit oder der Vorschlag konnte nicht gemacht werden. Das einzige Problem ist, dass wenn Sie sehen:

p->x = 3;

Sie wissen, dass p ein Zeiger ist, aber wenn Sie erlauben:

p.x = 3;

In diesem Fall wissen Sie nicht, was potentiell zu Problemen führen könnte, insbesondere wenn Sie diesen Zeiger später verwenden und die falsche Anzahl von Dereferenzierungsebenen verwenden.

OK, das hat keine ernsthafte Konsequenz, aber es nervt mich für eine Weile: Gibt es einen Grund für die Unterscheidung zwischen den -> und . Betreiber?

Natürlich ist die aktuelle Regel das . wirkt auf eine Struktur und -> wirkt auf einen Zeiger auf eine Struktur (oder Union). Aber so funktioniert es in der Praxis. Sei s eine Struktur, die ein Element x , und sei ps ein Zeiger auf eine Struktur derselben Form.

Wenn du schreibst

s->x

der Compiler wird eine Warnung in der Art ausspucken

Du meintest sx Bitte wiederhole das und kompiliere es neu.

Wenn du schreibst

ps.x

der Compiler wird eine Warnung in der Art ausspucken

Du meintest ps -> x. Bitte wiederholen Sie das und kompilieren Sie erneut.

Da der Compiler zum Zeitpunkt der Kompilierung den Typ von s und ps kennt, verfügt er über alle Informationen, die er benötigt, um den richtigen Operator zu interpretieren. Ich vermute, dass dies nicht wie andere Warnungen (wie ein fehlendes Semikolon) ist, da es keine Zweideutigkeit bezüglich der richtigen Lösung gibt.

Also hier ist ein hypothetischer Vorschlag an den C1x-Normenausschuss (der niemals in Erwägung gezogen würde, weil die ISO konservativ ist):

Wenn der Ausdruck lhs.rhs ist, wenn lhs ein Struktur- oder Vereinigungstyp ist, dann soll sich der Ausdruck auf das Element von lhs mit dem Namen rhs beziehen. Wenn lhs vom Typ pointer-to-struct oder -union ist, wird dies als (* lhs) .rhs interpretiert.

Das würde uns sicherlich alle Zeit ersparen und es den Leuten leichter machen, C zu lernen [und ich habe genug C gelehrt, um mit Autorität zu sagen, dass die Lernenden das -> Ding entweder verwirrend oder ärgerlich finden.]

Es gibt sogar einen Präzedenzfall, wo C eine Handvoll ähnlicher Dinge tut. Zum Beispiel werden Funktionsdeklarationen aus Gründen der Implementierung immer in pointer-to-function umgewandelt, so dass f(x,y) und (*f)(x,y) beide funktionieren, unabhängig davon, ob f als Funktion oder Zeiger deklariert wurde Funktionieren.

Also, meine Frage: Was ist falsch an diesem Vorschlag? Können Sie sich Beispiele sx , bei denen es zwischen ps.x und sx zu einer fatalen Mehrdeutigkeit ps.x sx , oder warum es sinnvoll ist, die obligatorische Unterscheidung sx ?


Answer #1

Ich glaube nicht, dass es etwas Verrücktes an dem gibt, was du gesagt hast. Verwenden . für Zeiger auf Strukturen würde funktionieren.

Ich mag jedoch die Tatsache, dass Zeiger auf Strukturen und Strukturen unterschiedlich behandelt werden.

Es gibt einen Zusammenhang über Operationen und Hinweise, was teuer sein könnte.

Betrachten Sie dieses Snippet, stellen Sie sich vor, dass es in der Mitte einer ziemlich großen Funktion ist.

s.c = 99;
f(s);

assert(s.c == 99);

Momentan kann ich sagen, dass s eine Struktur ist. Ich weiß, dass es für den Aufruf von f komplett kopiert wird. Ich weiß auch, dass diese Behauptung nicht schießen kann.

Wenn Sie verwenden . mit Zeigern zu struct waren erlaubt, ich würde nichts davon wissen und die Assert könnte feuern, f könnte sc (err s->c ) auf etwas anderes setzen.

Der andere Nachteil ist, dass es die Kompatibilität mit C ++ reduzieren würde. C ++ erlaubt -> von Klassen überladen zu werden, so dass Klassen "wie" Zeiger sein können. Es ist wichtig, dass . und -> verhalten sich anders. "Neuer" C-Code, der verwendet wurde . mit Zeigern auf Structs wäre wahrscheinlich nicht mehr als C ++ Code mehr akzeptabel.


Answer #2

Nun, wenn Sie diese Art von Funktionalität wirklich in die Spezifikation der C-Sprache einführen wollten, dann wäre es logisch, das Konzept von "Verfall zu Zeiger" zu erweitern, um es mit dem Rest der Sprache "zu verschmelzen" "zu Strukturtypen. Sie haben selbst ein Beispiel mit einer Funktion und einem Funktionszeiger gemacht. Der Grund dafür ist, dass der Funktionstyp in C in allen Kontexten in den Zeigertyp wechselt, mit Ausnahme von sizeof und unary & operators. (Das gleiche passiert mit Arrays, BTW.)

Um etwas Ähnliches wie das, was Sie vorgeschlagen haben, zu implementieren, könnten wir das Konzept des "Struct-To-Pointer-Decay" einführen, das genau so funktionieren würde wie alle anderen "Zerfälle" in C (nämlich array-to - Pointer-Decay und Function-To-Pointer-Decay) funktionieren: Wenn ein Strukturobjekt des Typs T in einem Ausdruck verwendet wird, verfällt sein Typ sofort in T* - Zeiger auf den Anfang des Strukturobjekts - außer wenn es ein Operand von ist sizeof oder unary & . Sobald eine solche Abklingregel für Strukturen eingeführt wurde, können Sie mit dem Operator -> auf Strukturelemente zugreifen, unabhängig davon, ob Sie auf der linken Seite einen Zeiger auf struct oder die Struktur selbst haben. Betreiber . wäre in diesem Fall völlig unnötig (es sei denn, ich verpasse etwas), würden Sie immer verwenden -> und nur -> .

Das oben, wieder einmal, wie dieses Feature meiner Meinung nach aussehen würde, wenn es im Geiste der Sprache C umgesetzt wurde.

Aber ich würde sagen (in Übereinstimmung mit dem, was Charles sagte), dass der Verlust der visuellen Unterscheidung zwischen dem Code, der mit Pointern auf Strukturen arbeitet, und dem Code, der mit Strukturen selbst arbeitet, nicht gerade wünschenswert ist.

PS Eine offensichtliche negative Konsequenz einer solchen Zerfallsregel für Strukturen wäre, dass neben der aktuellen Armee von Neulingen, die selbstlos glauben, dass "Arrays nur konstante Zeiger" sind, wir eine Armee von Neulingen haben, die selbstlos glauben, dass "Strukturobjekte nur konstante Zeiger sind ". Und Chris Toreks Array-FAQ müsste etwa 1,5-2x größer sein, um auch Strukturen abzudecken :)


Answer #3

Ein Unterscheidungsmerkmal der C-Programmiersprache (im Gegensatz zu ihrem relativen C ++) ist, dass das Kostenmodell sehr explizit ist . Der Punkt unterscheidet sich von dem Pfeil, da der Pfeil eine zusätzliche Speicherreferenz erfordert, und C ist sehr vorsichtig, um die Anzahl der Speicherreferenzen aus dem Quellcode ersichtlich zu machen.


Answer #4

Die aktuelle Syntax ermöglicht den Lesern des Codes, ob der Code mit einem Zeiger oder dem tatsächlichen Objekt arbeitet. Jemand, der den Code vorher nicht kennt, versteht es besser.


Answer #5

Nun, es könnte durchaus Fälle geben, in denen Sie etwas Komplexes haben wie:

(*item)->elem

(was ich in einigen Programmen passiert habe), und wenn du sowas geschrieben hast

item.elem

Das Obige bedeutet, dass es verwirrend sein könnte, ob elem ein Element von struct item ist oder ein Element einer struct, auf das item zeigt, oder ein Element einer struct, das auf ein Element in einer Liste zeigt, auf die von einem Punkt verwiesen wird Iterator-Element und so weiter und so fort.

Also ja, es macht die Dinge etwas klarer, wenn man Zeiger auf Zeiger auf Strukturen usw. verwendet.







struct