Ich möchte UTF-8 in C ++ analysieren. Wenn ich ein neues Zeichen parsiere, weiß ich nicht im Voraus, ob es ein ASCII-Byte oder der Anführer eines Multibyte-Zeichens ist, und ich weiß auch nicht, ob meine Eingabezeichenfolge ausreichend lang ist, um die restlichen Zeichen zu enthalten.
Der Einfachheit halber möchte ich die vier nächsten Bytes a
, b
, c
und d
nennen, und weil ich in C ++ bin, möchte ich das mit Referenzen machen.
Ist es zulässig, diese Referenzen zu Beginn einer Funktion zu definieren, solange ich nicht darauf zugreife, bevor ich weiß, dass der Zugriff sicher ist? Beispiel:
%Vor%Das obige Beispiel zeigt, was ich semantisch machen möchte. Es zeigt nicht, warum ich das machen möchte, aber offensichtlich wird echter Code mehr involviert sein, also definiere ich b, c, d nur, wenn ich weiß, dass der Zugriff sicher ist und ich sie zu ausführlich brauche.
Es gibt drei Möglichkeiten:
Formal
nun, wer weiß. Ich könnte es für dich herausfinden, indem ich einige Zeit darauf verwende, aber dann könntest du es auch tun. Oder irgendein Leser. Und das ist nicht sehr nützlich.
BEARBEITEN OK, schau es dir an, denn du scheinst nicht glücklich darüber zu sein, dass ich das Formale erwähnt habe, ohne es dir aufgefallen zu sein. Formal haben Sie Pech:
N3280 (C ++ 11) §5.7 / 5 "Zeigen sowohl der Zeigeroperand als auch das Ergebnis auf Elemente desselben Array-Objekts, oder eine Vergangenheit
das letzte Element des Array-Objekts, soll die Auswertung keinen Überlauf erzeugen; Andernfalls ist das Verhalten nicht definiert. "
Zwei Situationen, in denen dies zu unerwünschtem Verhalten führen kann: (1) Berechnen einer Adresse über das Ende eines Segments hinaus und (2) Berechnen einer Adresse außerhalb eines Arrays, dessen Compiler die Größe kennt, mit aktivierten Debug-Prüfungen.
Technisch Sie sind wahrscheinlich in Ordnung, solange Sie eine lvalue-to-rvalue-Konvertierung vermeiden, denn wenn die Referenzen als Zeiger implementiert sind, dann ist es genauso sicher wie Zeiger, und wenn der Compiler sie als Aliase implementiert, ist das auch in Ordnung .
Wirtschaftlich Sich unnötig auf eine Subtilität zu verlassen verschwendet Ihre Zeit und dann auch die Zeit anderer, die sich mit dem Code beschäftigen. Also, keine gute Idee . Erklären Sie stattdessen die Namen, wenn garantiert ist, dass sie darauf verweisen.
Bevor Sie auf die Rechtmäßigkeit von Verweisen auf nicht zugreifbaren Speicher eingehen, haben Sie ein anderes Problem in Ihrem Code. Ihr Aufruf an s[i+x]
könnte string::operator[]
mit einem größeren Parameter als s.size()
aufrufen. Der C ++ 11-Standard sagt über string::operator[]
( [string.access], §21.4.5 ):
Benötigt: pos & lt; = size ().
Rückgabe: * (begin () + pos) wenn pos & lt; size (), ansonsten eine Referenz auf ein Objekt vom Typ T mit dem Wert charT (); Der referenzierte Wert darf nicht verändert werden.
Dies bedeutet, dass der Aufruf von s[x]
für x > s.size()
undefiniertes Verhalten ist, so dass die Implementierung Ihr Programm sehr gut beenden kann, z. dafür eine Behauptung.
Da string
jetzt garantiert fortlaufend ist, könntest du das Problem mit & amp; s [i] + x umgehen, um eine Adresse zu erhalten. In der Praxis wird das wahrscheinlich funktionieren.
Streng genommen ist das aber leider immer noch illegal. Der Grund dafür ist, dass der Standard Zeigerarithmetik nur erlaubt, solange der Zeiger im selben Array oder nach dem Ende des Arrays bleibt. Der relevante Teil des (C ++ 11) Standards ist in [expr.add], §5.7.5:
Zeigen sowohl der Zeigeroperand als auch das Ergebnis auf Elemente desselben Arrayobjekts oder auf ein Element nach dem letzten Element des Arrayobjekts, soll die Auswertung keinen Überlauf erzeugen; Andernfalls ist das Verhalten nicht definiert.
Das Generieren von Verweisen oder Zeigern auf ungültige Speicherstellen funktioniert daher möglicherweise bei den meisten Implementierungen, aber es ist ein technisch undefiniertes Verhalten, auch wenn Sie den Zeiger nie dereferenzieren / den Verweis verwenden. Sich auf UB zu verlassen, ist fast nie eine gute Idee, denn selbst wenn es für alle Zielsysteme funktioniert, gibt es keine Garantie dafür, dass es auch in Zukunft funktioniert.
Grundsätzlich ist die Idee, eine Referenz für eine möglicherweise illegale Speicheradresse zu nehmen, selbst vollkommen legal. Die Referenz ist nur ein Zeiger unter der Haube, und Zeigerarithmetik ist zulässig, bis Dereferenzierung auftritt.
EDIT: Dieser Anspruch ist ein praktischer, nicht von dem veröffentlichten Standard abgedeckt. Es gibt viele Ecken des veröffentlichten Standards, die formal undefiniert sind, aber in der Praxis kein unerwartetes Verhalten hervorrufen.
Nehmen wir zum Beispiel die Möglichkeit, einen Zeiger auf das zweite Objekt nach dem Ende eines Arrays zu berechnen (wie @DanielTrebbien suggeriert). Der Standard besagt, dass Überlauf zu undefiniertem Verhalten führen kann. In der Praxis würde der Überlauf nur auftreten, wenn das obere Ende des Arrays knapp vor dem durch einen Zeiger adressierbaren Platz liegt. Kein wahrscheinliches Szenario. Selbst wenn es passieren sollte, würde auf den meisten Architekturen nichts Schlimmes passieren. Was verletzt wird, sind gewisse Garantien über Zeigerunterschiede, die hier nicht gelten.
@JoSo Wenn Sie mit einem Zeichenarray arbeiten, können Sie einen Teil der Unsicherheit bezüglich der Referenzsemantik vermeiden, indem Sie die const-Referenzen durch const-Zeiger in Ihrem Code ersetzen. Auf diese Weise können Sie sicher sein, dass kein Compiler die Werte aliasiert.
Tags und Links c++