Ich hatte gehofft, dass jemand genau erklären könnte, was mit undefiniertem Verhalten in C ++ gemeint ist. Angesichts der folgenden Klassendefinition:
%Vor%Wenn ich richtig verstanden habe, entfernen die beiden const_casts sowohl für einen Verweis als auch für einen Zeiger im folgenden Code die Const-ness des ursprünglichen Objekts vom Typ Foo, aber alle Versuche, dieses Objekt durch den Zeiger oder zu ändern Die Referenz führt zu undefiniertem Verhalten.
%Vor%Was mich verwirrt, ist der Grund, warum dies zu funktionieren scheint und das ursprüngliche const-Objekt modifizieren wird, aber mich nicht einmal mit einer Warnung dazu aufruft, mir mitzuteilen, dass dieses Verhalten nicht definiert ist. Ich weiß, dass Const_casts im Großen und Ganzen verpönt sind, aber ich kann mir einen Fall vorstellen, in dem die Unkenntnis, dass C-Style-Cast zu einem const_cast führen kann, ohne bemerkt zu werden, zum Beispiel:
%Vor%Unter welchen Umständen kann dieses Verhalten einen schwerwiegenden Laufzeitfehler verursachen? Gibt es eine Compiler-Einstellung, die ich einstellen kann, um mich vor diesem (potenziell) gefährlichen Verhalten zu warnen?
NB: Ich benutze MSVC2008.
Ich hatte gehofft, dass jemand genau erklären könnte, was mit undefiniertem Verhalten in C ++ gemeint ist.
Technisch gesehen bedeutet "nicht definiertes Verhalten", dass die Sprache keine Semantik definiert, um so etwas zu tun.
In der Praxis bedeutet dies normalerweise " tu es nicht ; es kann brechen, wenn der Compiler Optimierungen vornimmt oder aus anderen Gründen".
Was mich verwirrt, ist warum das scheinbar funktioniert und das ursprüngliche const-Objekt ändert, aber ich werde nicht einmal mit einer Warnung darauf hingewiesen, dass dieses Verhalten nicht definiert ist.
In diesem speziellen Beispiel scheint der Versuch, ein nicht veränderbares Objekt zu ändern, scheinbar "zu funktionieren", oder es überschreibt Speicher, der nicht zum Programm gehört oder zu einem anderen Objekt gehört, weil der Nicht veränderbare Objekte wurden möglicherweise zur Kompilierungszeit entfernt oder in einigen schreibgeschützten Datensegmenten im Speicher vorhanden.
Die Faktoren, die dazu führen können, dass diese Dinge passieren, sind einfach zu komplex, um sie aufzulisten. Betrachten Sie den Fall der Dereferenzierung eines nicht initialisierten Zeigers (auch UB): Das "Objekt", mit dem Sie arbeiten, wird eine beliebige Speicheradresse haben, die davon abhängt, welcher Wert sich im Speicher des Zeigers befindet; Dieser "Wert" ist möglicherweise abhängig von früheren Programmaufrufen, früheren Arbeiten im selben Programm, Speicherung von vom Benutzer bereitgestellten Eingaben usw. Es ist einfach nicht möglich, zu versuchen, die möglichen Ergebnisse der Aufdeckung von Undefined Behavior zu rationalisieren. t störe und sage stattdessen " mach es nicht ".
Was mich verwirrt, ist, warum dies funktioniert und das ursprüngliche const-Objekt ändert, aber mich nicht einmal mit einer Warnung dazu auffordert, mir mitzuteilen, dass dieses Verhalten nicht definiert ist.
Als weitere Komplikation sind Compiler nicht erforderlich für die Diagnose (Warnungen / Fehler ausgeben) für undefiniertes Verhalten, weil Code, der Undefined Behavior aufruft, nicht mit Code identisch ist, der schlecht formatiert ist (d ausdrücklich illegal). In vielen Fällen ist es für den Compiler nicht möglich, sogar UB zu erkennen, daher ist dies ein Bereich, in dem der Programmierer den Code richtig schreiben muss.
Das Typsystem - einschließlich Existenz und Semantik des Schlüsselworts const
- bietet grundlegenden Schutz gegen das Schreiben von Code, der bricht; Ein C ++ - Programmierer sollte sich stets bewusst sein, dass dieses System - z. indem Sie const
ness hacken - geschieht auf eigene Gefahr und ist in der Regel eine schlechte Idee. ™
Ich kann mir einen Fall vorstellen, in dem es nicht bewusst ist, dass C-Style-Cast dazu führen kann, dass ein
const_cast
gemacht wird, ohne bemerkt zu werden.
Absolut. Wenn die Warnungsstufen hoch genug eingestellt sind, kann ein vernünftiger Compiler Sie davor warnen, aber es muss nicht und darf es auch nicht. Im Allgemeinen ist dies ein guter Grund, warum C-Style Casts verpönt sind, aber sie werden immer noch unterstützt, um rückwärtskompatibel mit C zu sein. Es ist nur eines dieser unglücklichen Dinge.
Undefiniertes Verhalten bedeutet genau das: Verhalten, das nicht durch den Sprachstandard definiert ist. Es tritt normalerweise in Situationen auf, in denen der Code etwas falsch macht, aber der Fehler kann nicht vom Compiler erkannt werden. Die einzige Möglichkeit, den Fehler zu finden, wäre die Einführung eines Laufzeittests, der die Leistung beeinträchtigen würde. Stattdessen sagt Ihnen die Sprachspezifikation, dass Sie bestimmte Dinge nicht tun dürfen, und wenn Sie dies tun, dann könnte alles passieren.
Wenn Sie in ein konstantes Objekt schreiben, indem Sie const_cast
verwenden, um die Prüfungen zur Kompilierungszeit zu unterlaufen, gibt es drei mögliche Szenarien:
In Ihrem Test landeten Sie im ersten Szenario - das Objekt wurde (fast sicher) auf dem Stack erstellt, der nicht schreibgeschützt ist. Sie können feststellen, dass Sie das zweite Szenario erhalten, wenn das Objekt statisch ist, und das dritte, wenn Sie mehr Optimierung aktivieren.
Im Allgemeinen kann der Compiler diesen Fehler nicht diagnostizieren - es gibt keine Möglichkeit zu sagen (außer in sehr einfachen Beispielen wie Ihrem), ob das Ziel einer Referenz oder eines Zeigers konstant ist oder nicht. Es liegt an Ihnen, sicherzustellen, dass Sie const_cast
nur verwenden, wenn Sie garantieren können, dass es sicher ist - entweder wenn das Objekt nicht konstant ist oder wenn Sie es sowieso nicht ändern.
Was mich verwirrt, ist, warum dies scheint zu funktionieren
Das ist, was undefiniertes Verhalten bedeutet.
Es kann alles tun, einschließlich scheinen, zu funktionieren.
Wenn Sie das Optimierungslevel auf den höchsten Wert erhöhen, wird es wahrscheinlich nicht mehr funktionieren.
, gibt mir aber nicht einmal eine Warnung, dass dieses Verhalten nicht definiert ist.
An der Stelle, wo es war, ist die Modifikation das Objekt nicht const. Im allgemeinen Fall kann es nicht sagen, dass das Objekt ursprünglich ein const war, deshalb ist es nicht möglich, Sie zu warnen. Auch wenn es sich um eine Aussage handelte, wird diese unabhängig von den anderen ausgewertet (wenn man sich diese Art von Warngenerierung anschaut).
Zweitens, indem Sie cast verwenden, sagen Sie dem Compiler "Ich weiß, was ich mache, überschreibe alle Sicherheitsfunktionen und tue es einfach" .
Zum Beispiel funktioniert das folgende gut: (oder wird auch scheinen (in der nasalen Deamon Art von Weg))
%Vor%Ich weiß, dass const_casts im Großen und Ganzen verpönt sind auf
Das ist der falsche Weg, sie anzusehen. Sie sind eine Möglichkeit, in dem Code zu dokumentieren, dass Sie etwas Seltsames tun, das von intelligenten Leuten validiert werden muss (da der Compiler der Besetzung ohne Frage gehorcht). Der Grund, warum Sie eine intelligente Person zur Validierung benötigen, ist, dass sie zu undefiniertem Verhalten führen kann, aber das Gute, was Sie jetzt explizit in Ihrem Code dokumentiert haben (und die Leute werden sich genau anschauen, was Sie getan haben).
aber ich kann mir einen Fall vorstellen, in dem die Ungewissheit, dass C-Style-Cast dazu führen kann, dass ein const_cast gemacht wird, ohne bemerkt zu werden, zum Beispiel:
In C ++ muss kein C-Style-Cast verwendet werden.
Im schlimmsten Fall kann der C-Style Cast durch reinterpret_cast & lt; & gt; Aber wenn Sie Code portieren, möchten Sie sehen, ob Sie static_cast & lt; & gt; verwendet haben könnten. Der Punkt der C ++ - Umwandlungen besteht darin, sie hervorzuheben , damit Sie sie sehen und auf einen Blick den Unterschied zwischen den gefährlichen und den gutartigen Umwandlungen erkennen können.
Undefiniertes Verhalten hängt davon ab, wie das Objekt erstellt wurde . Sie können Stephan erklären es um 00:10:00, aber im Wesentlichen, folgen Sie den folgenden Code:
%Vor% Nun gibt es zwei Fälle für den Aufruf von f
Zusammenfassend wurde K
als non const geboren, also ist die Besetzung in Ordnung, wenn f aufgerufen wird, während AK
als const
geboren wurde, also ... UB ist es.
Ein klassisches Beispiel wäre der Versuch, ein Konst-String-Literal zu ändern, das in einem geschützten Datensegment vorhanden sein kann.
Statische und const Daten werden oft in einem anderen Teil Ihres Programms gespeichert als lokale Variablen. Bei const-Variablen befinden sich diese Bereiche häufig im schreibgeschützten Modus, um die Konstanz der Variablen zu erzwingen. Der Versuch, in einen Nur-Lese-Speicher zu schreiben, führt zu einem "undefinierten Verhalten", da die Reaktion von Ihrem Betriebssystem abhängt. "Undefined beheavior" bedeutet, dass die Sprache nicht angibt, wie dieser Fall behandelt werden soll.
Wenn Sie eine ausführlichere Erklärung zum Speicher wünschen, empfehle ich Ihnen dies . Es ist eine Erklärung basierend auf UNIX, aber ähnliche Mechanismen werden auf allen Betriebssystemen verwendet.
Tags und Links c++ undefined-behavior const-cast