Die allgemeine Antwort auf die Frage "Wie implementiert man die memcpy-Funktion mit strengen Aliasing-Regeln?" ist etwas nach dem Motto
%Vor%Wenn ich jedoch richtig verstanden habe, kann der Compiler den Aufruf von memcpy und den Zugriff auf das Ziel neu anordnen, da er Schreibvorgänge mit allen anderen Zeigertypen in char * umordnen kann (strenge Aliasregeln verhindern nur die Neuanordnung von Lesevorgängen aus char * mit Schreibzugriff auf einen anderen Zeigertyp).
Ist das korrekt und wenn ja, gibt es irgendwelche Möglichkeiten, memcpy korrekt zu implementieren, oder sollten wir uns nur auf eingebaute memcpy verlassen?
Bitte beachten Sie, dass es sich bei dieser Frage nicht nur um Memcpy, sondern auch um Deserialisierung / Decodierung handelt.
Die strikte Aliasing-Regel schließt Umwandlungen von char
-Typen aus (siehe letzten Aufzählungspunkt unten), damit der Compiler in Ihrem Fall das Richtige tut. Typ punning ist nur ein Problem beim Konvertieren von Dingen wie int
in short
. Hier kann der Compiler Annahmen treffen, die zu undefiniertem Verhalten führen.
C99 § 6.5 / 7:
Auf einen gespeicherten Wert eines Objekts darf nur ein lvalue-Ausdruck zugreifen, der einen der folgenden Typen aufweist:
- ein Typ, der mit dem effektiven Objekttyp kompatibel ist,
- eine quali fi zierte Version eines Typs, der mit dem effektiven Objekttyp
kompatibel ist- ein Typ, bei dem es sich um den Typ mit oder ohne Vorzeichen handelt, der dem effektiven Typ des Objekts entspricht,
- ein Typ, bei dem es sich um den Typ mit oder ohne Vorzeichen handelt, der einer quali fi zierten Version des effektiven Objekttyps
entspricht- ein Aggregat- oder Unionstyp, der einen der oben genannten Typen unter seinen Mitgliedern enthält (einschließlich rekursiv eines Mitglieds eines Unteraggregats oder einer enthaltenen Union) oder
- ein Zeichentyp
Da sowohl (char*)dest
als auch (char const*)src
auf char
zeigen, muss der Compiler davon ausgehen, dass sie Aliasnamen haben. Außerdem gibt es eine Regel, die besagt, dass ein Zeiger auf einen Zeichentyp einen beliebigen Aliasnamen haben kann.
All dies ist für memcpy
irrelevant, da die tatsächliche Signatur lautet:
sagt dem Compiler, dass kein Aliasing möglich ist, weil der Benutzer dies garantiert. Sie können memcpy
nicht verwenden, um überlappende Bereiche zu kopieren, ohne ein undefiniertes Verhalten zu verursachen.
Auf jeden Fall gibt es kein Problem mit der gegebenen Implementierung.
IANALL, aber ich denke nicht, dass der Compiler Dinge in der Art durcheinander bringen darf, wie Sie es beschreiben. Striktes Aliasing wird in der Spezifikation "implementiert", indem nicht definierte Zugriffe auf ein Objekt durch einen ungültigen Zeigertyp gerendert werden, anstatt eine weitere komplizierte Teilaufordnung bei Objektzugriffen anzugeben.
Was hier anscheinend fehlt, ist, dass das strikte Aliasing (6.5 / 7) vom Begriff effektiver Typ (6.5 / 6) abhängt. Und der effektive Typ hat explizite, spezielle Regeln für die Funktion memcpy
(6.5 / 6):
Wenn ein Wert in ein Objekt kopiert wird, das keinen deklarierten Typ hat, verwenden Sie
memcpy
odermemmove
, oder wird als ein Array von Zeichentypen kopiert, dann ist der effektive Typ des modifizierten Objekts für diesen Zugriff und für nachfolgende Zugriffe, die den Wert nicht ändern, der effektive Typ des Objekts, von dem der Wert stammt wird kopiert, wenn es eins hat.
Also ich denke nicht, dass es sogar sinnvoll ist, innerhalb der Funktion memcpy
von einem strikten Aliasing zu sprechen. Sie können nur von strengem Aliasing sprechen, wenn Sie den effektiven Typ kennen. Nun, wie bestimmen Sie das, basierend auf dem oben genannten? Ist die Interna von memcpy
eine Kopie mit memcpy
oder nicht?
Es ist so, als würde man sagen: "Um zu verstehen, welcher effektive Typ in memcpy verwendet wird, müssen Sie zuerst verstehen, welcher effektive Typ in memcpy verwendet wird".
Ich sehe also nicht genau, wie die Frage oder irgendeine der geposteten Antworten einen Sinn ergibt.
Ja, du verpasst etwas. Der Compiler kann Schreibvorgänge in dest
neu anordnen und liest in dest
. Jetzt, da von src
happed vor dem Schreiben in dest
gelesen wird und Ihr hypothetischer Lesevorgang von dest
passiert - nach dem Schreiben in dest
folgt daraus, dass der Lesevorgang von dest
nach dem Lesen von erfolgt %Code%.
Wenn ein Objekt keinen deklarierten Typ hat, ist jeder effektive Typ, den es erhalten kann, nur wirksam, bis das Objekt das nächste Mal geändert wird. Das Schreiben in ein Objekt unter Verwendung eines Zeigers des Zeichentyps zählt als Änderung, wodurch der alte Typ unscharf geschaltet wird, aber das Schreiben über den Zeichen-Typ-Zeiger setzt keinen neuen Typ, es sei denn, dieser Vorgang erfolgt als Teil des Kopierens als ein Feld eines Zeichentyps ", Was auch immer das heißt. Objekte, die keinen effektiven Typ haben, dürfen mit jedem Typ legal gelesen werden.
Da die Semantik des effektiven Typs für "Kopieren als ein Array von Zeichentypen" mit der von memcpy
identisch ist, könnte eine memcpy-Implementierung unter Verwendung von Zeichenzeigern zum Lesen und Schreiben geschrieben werden. Es könnte den effektiven Typ des Ziels nicht so festlegen, wie memcpy
erlaubt wäre, aber jedes Verhalten, das bei Verwendung von memcpy
definiert wäre, wäre identisch definiert, wenn das Ziel keinen effektiven Typ hätte [wie IMHO es haben sollte war der Fall mit memcpy
].
Ich bin nicht sicher, wer die Idee hatte, dass ein Compiler annehmen kann, dass Speicher, der einen effektiven Typ angenommen hat, diesen effektiven Typ beibehält, wenn er mit char*
modifiziert wird, aber nichts im Standard begründet dies. Wenn Sie Ihren Code für die Arbeit mit gcc benötigen, geben Sie an, dass er mit dem -fno-strict-aliasing
-Flag verwendet werden muss, es sei denn, oder bis gcc beginnt, den Standard zu honorieren. Es gibt keinen Grund, sich rückwärts zu beugen und einen Compiler zu unterstützen, dessen Autoren ständig neue Fälle suchen, um Aliasing zu ignorieren, selbst in Fällen, in denen der Standard sie dazu zwingen würde, sie zu erkennen.
Tags und Links c c++ memcpy strict-aliasing