Die Antwort lautet: Es hängt davon ab, gegen welchen C ++ - Standard Sie kompilieren. Der gesamte Code ist über alle Standards hinweg perfekt formuliert ‡ mit Ausnahme dieser Zeile:
char * s = "My String";
Jetzt hat das Zeichenfolgenliteral den Typ const char[10]
und wir versuchen, einen nicht konstanten Zeiger darauf zu initialisieren. Für alle anderen Typen außer der char
Familie der String-Literale war eine solche Initialisierung immer illegal. Beispielsweise:
const int arr[] = {1};
int *p = arr; // nope!
In Pre-C ++ 11 gab es jedoch für String-Literale eine Ausnahme in §4.2 / 2:
Ein String-Literal (2.13.4), das kein breites String-Literal ist, kann in einen Wert vom Typ " Zeiger auf Zeichen" konvertiert werden . [...]. In beiden Fällen ist das Ergebnis ein Zeiger auf das erste Element des Arrays. Diese Konvertierung wird nur berücksichtigt, wenn ein explizit geeigneter Zeigerzieltyp vorhanden ist, und nicht, wenn generell eine Konvertierung von einem l-Wert in einen r-Wert erforderlich ist. [Hinweis: Diese Konvertierung ist veraltet . Siehe Anhang D. ]
In C ++ 03 ist der Code also vollkommen in Ordnung (obwohl veraltet) und weist ein klares, vorhersehbares Verhalten auf.
In C ++ 11 existiert dieser Block nicht - es gibt keine solche Ausnahme für String-Literale, in die konvertiert wurde char*
, und daher ist der Code genauso schlecht geformt wie derint*
Beispiel, das ich gerade bereitgestellt habe. Der Compiler ist verpflichtet, eine Diagnose zu stellen, und im Idealfall würden wir in solchen Fällen, bei denen es sich eindeutig um Verstöße gegen das System vom Typ C ++ handelt, erwarten, dass ein guter Compiler diesbezüglich nicht nur konform ist (z. B. durch Ausgabe einer Warnung), sondern auch fehlschlägt geradezu.
Der Code sollte im Idealfall nicht kompiliert werden - aber sowohl auf gcc als auch auf clang (ich nehme an, da es wahrscheinlich viel Code gibt, der mit geringem Gewinn gebrochen werden würde, obwohl diese Art von Systemloch seit über einem Jahrzehnt veraltet ist). Der Code ist schlecht geformt, und daher ist es nicht sinnvoll, über das Verhalten des Codes nachzudenken. Aber angesichts dieses speziellen Falls und der Geschichte, in der er zuvor erlaubt war, halte ich es nicht für unangemessen, den resultierenden Code so zu interpretieren, als wäre er implizit const_cast
, so etwas wie:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
Damit ist der Rest des Programms vollkommen in Ordnung, da Sie nie s
wieder berühren . Das Lesen eines erstellten const
Objekts über einen Nichtzeiger const
ist vollkommen in Ordnung. Das Schreiben eines erstellten const
Objekts über einen solchen Zeiger ist ein undefiniertes Verhalten:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Da es an keiner s
Stelle in Ihrem Code Änderungen gibt, ist das Programm in C ++ 03 in Ordnung, sollte in C ++ 11 nicht kompiliert werden können, tut es aber trotzdem - und da die Compiler dies zulassen, gibt es immer noch kein undefiniertes Verhalten darin † . Angesichts der Tatsache, dass die Compiler die C ++ 03-Regeln immer noch [falsch] interpretieren, sehe ich nichts, was zu "unvorhersehbarem" Verhalten führen würde. Schreiben Sie s
aber, und alle Wetten sind aus. In C ++ 03 und C ++ 11.
† Auch wenn per Definition schlecht geformter Code keine Erwartung eines angemessenen Verhaltens ergibt.
‡ Außer nicht, siehe Matt McNabbs Antwort