Warum ist die Konvertierung von einer Zeichenfolgenkonstante in 'char *' in C gültig, in C ++ jedoch ungültig?

162

Der C ++ 11-Standard (ISO / IEC 14882: 2011) lautet § C.1.1:

char* p = "abc"; // valid in C, invalid in C++

Für C ++ ist es in Ordnung, da ein Zeiger auf ein String-Literal schädlich ist, da jeder Versuch, es zu ändern, zu einem Absturz führt. Aber warum ist es in C gültig?

Das C ++ 11 sagt auch:

char* p = (char*)"abc"; // OK: cast added

Das heißt, wenn eine Besetzung zur ersten Anweisung hinzugefügt wird, wird sie gültig.

Warum macht das Casting die zweite Anweisung in C ++ gültig und wie unterscheidet sie sich von der ersten? Ist es nicht immer noch schädlich? Wenn dies der Fall ist, warum hat der Standard dann gesagt, dass es in Ordnung ist?

rullof
quelle
3
C ++ 11 erlaubt den ersten nicht. Ich habe keine Ahnung, warum C den Typ eines String-Literals überhaupt gemacht char[]hat. Der zweite ist eine const_castVerkleidung.
Chris
4
Es gibt einfach zu viel alten C-Code, der brechen würde, wenn diese Regel geändert würde.
Paul R
1
Bitte zitieren Sie den Text, in dem der Standard den zweiten sagt OK.
Nawaz
13
Die C-Sprache hatte vorher String-Literale const, also waren sie es notwendigerweise nicht const.
Casey
2
Mit C und C ++ können Sie von nahezu jedem Typ in einen anderen Typ umwandeln. Das bedeutet nicht, dass diese Abgüsse sinnvoll und sicher sind.
Siyuan Ren

Antworten:

206

Bis C ++ 03 war Ihr erstes Beispiel gültig, verwendete jedoch eine veraltete implizite Konvertierung. Ein Zeichenfolgenliteral sollte als typisiert behandelt werden char const *, da Sie seinen Inhalt nicht ändern können (ohne undefiniertes Verhalten zu verursachen).

Ab C ++ 11 wurde die implizite Konvertierung, die veraltet war, offiziell entfernt, sodass davon abhängiger Code (wie in Ihrem ersten Beispiel) nicht mehr kompiliert werden sollte.

Sie haben einen Weg gefunden, wie der Code kompiliert werden kann: Obwohl die implizite Konvertierung entfernt wurde, funktioniert eine explizite Konvertierung weiterhin, sodass Sie eine Besetzung hinzufügen können. Ich würde dies jedoch nicht als "Korrektur" des Codes betrachten.

Um den Code wirklich zu korrigieren, muss der Zeigertyp auf den richtigen Typ geändert werden:

char const *p = "abc"; // valid and safe in either C or C++.

Warum es in C ++ erlaubt war (und immer noch in C ist): einfach, weil es eine Menge vorhandenen Codes gibt, der von dieser impliziten Konvertierung abhängt, und das Brechen dieses Codes (zumindest ohne offizielle Warnung) schien den Standardausschüssen anscheinend zu gefallen eine schlechte Idee.

Jerry Sarg
quelle
8
@rullof: Es ist gefährlich genug, dass es keine sinnvolle Flexibilität bietet, zumindest für Code, der sich (überhaupt) um Portabilität kümmert. Wenn Sie in ein Zeichenfolgenliteral schreiben, wird Ihr Programm normalerweise auf einem modernen Betriebssystem abgebrochen. Wenn Sie also zulassen, dass Code dort schreibt (versucht), bedeutet dies keine sinnvolle Flexibilität.
Jerry Coffin
3
Die Code - Snippet in dieser Antwort gegeben char const *p = "abc";„gültig und sicher in sowohl C und C ++“, nicht „gültig und sicher in beiden C oder C ++“.
Daniel Le
4
@ DanielLe beide Sätze haben die gleiche Bedeutung
Caleth
3
Oh mein Gott! [Zunge fest in die Wange stecken] Entschuldigung, aber "oder" ist hier der richtige Begriff. Der Code kann entweder als C oder als C ++ kompiliert werden, kann jedoch nicht gleichzeitig als C und C ++ kompiliert werden. Sie können entweder wählen, aber Sie müssen eine Wahl treffen. Sie können nicht beide gleichzeitig haben. [normale Zungenoperation wieder aufnehmen].
Jerry Coffin
2
Nein, beide / und sind hier der klarste und korrekteste Wortlaut. Entweder / oder vermittelt auch die richtige Bedeutung, aber technisch ist es nicht so klar. Oder allein ist unwiderlegbar falsch ( A oder B ist nicht gleich A und B ).
Apollys unterstützt Monica
15

Es ist aus historischen Gründen in C gültig. C traditionell festgelegt , dass der Typ eines Stringliteral war char *nicht const char *, obwohl sie es qualifiziert , indem er sagte , dass Sie nicht wirklich , es zu ändern erlaubt.

Wenn Sie eine Besetzung verwenden, teilen Sie dem Compiler im Wesentlichen mit, dass Sie besser als die Standardregeln für die Typübereinstimmung Bescheid wissen, und die Zuweisung wird dadurch in Ordnung.

Barmar
quelle
3
Es war ein char[N]und wurde geändert in const char[N]. Es sind Größeninformationen beigefügt.
Chris
1
In C Typ der Zeichenfolge Literal ist char[N]aber nicht char*zB "abc"istchar[4]
Grijesh Chauhan
2

Sie können auch strdup verwenden :

char* p = strdup("abc");
baz
quelle