Warum erlauben Compiler String-Literale, nicht const zu sein?

8

Und wo genau sind Literale im Speicher? (Siehe Beispiele unten)

Ich kann ein Literal nicht ändern, also wäre es vermutlich ein const char *, obwohl der Compiler mich ein char * dafür verwenden lässt, ich habe keine Warnungen selbst bei den meisten Compiler-Flags.

Während eine implizite Umwandlung eines const char * -Typs in einen char * -Typ mich warnt, siehe unten (getestet auf GCC, verhält sich aber ähnlich auf VC ++ 2010).

Wenn ich auch den Wert eines const char ändere (mit einem Trick, unter dem GCC besser warnen kann), gibt es keinen Fehler und ich kann ihn sogar ändern und auf GCC anzeigen (obwohl ich es vermute) ist immer noch ein undefiniertes Verhalten, ich frage mich, warum es nicht das gleiche mit dem Literal gemacht hat). Deshalb frage ich , wo diese Literale gespeichert werden und wo häufiger const angeblich gespeichert wird?

%Vor%     
Dpp 19.06.2010, 09:38
quelle

6 Antworten

11

Der C-Standard verbietet nicht die Änderung von String-Literalen. Es sagt nur, dass das Verhalten nicht definiert ist, wenn der Versuch unternommen wird. Laut der C99-Begründung gab es Leute im Komitee, die wollten, dass Stringliterale modifizierbar sein sollten, so dass der Standard dies nicht ausdrücklich verbietet.

Beachten Sie, dass die Situation in C ++ anders ist. In C ++ sind String-Literale Arrays von const char. C ++ erlaubt jedoch Konvertierungen von const char * nach char *. Diese Funktion wurde jedoch eingestellt.

    
hrnt 19.06.2010, 09:51
quelle
2

Meistens historische Gründe. Beachten Sie jedoch, dass sie etwas gerechtfertigt sind: String-Literale haben nicht den Typ char * , sondern char [N] , wobei N die Größe des Puffers angibt (andernfalls würde sizeof bei String-Literalen nicht wie erwartet funktionieren ) und kann verwendet werden, um nicht const Arrays zu initialisieren. Sie können sie nur wegen der impliziten Konvertierungen von Arrays zu Zeigern und nicht const zu const zu const Zeigern zuordnen.

Es wäre konsistenter, wenn String-Literale das gleiche Verhalten wie zusammengesetzte Literale hätten, aber da es sich um ein C99-Konstrukt handelt und die Rückwärtskompatibilität beibehalten werden musste, war dies keine Option, so dass String-Literale ein Ausnahmefall bleiben.

    
Christoph 19.06.2010 14:24
quelle
1
  

Und wo genau sind Literale im Speicher? (Siehe Beispiele unten)

Initialisiertes Datensegment Unter Linux ist es entweder .data oder .rodata .

  

Ich kann ein Literal nicht ändern, also wäre es vermutlich ein const char *, obwohl der Compiler mich ein char * dafür verwenden lässt, ich habe keine Warnungen selbst bei den meisten Compiler-Flags.

Historisch wie es schon von anderen erklärt wurde. Bei den meisten Compilern können Sie angeben, ob die Zeichenfolgenliterale mit einer Befehlszeilenoption schreibgeschützt oder änderbar sein sollen.

Der Grund, warum String-Literale im Allgemeinen schreibgeschützt sein sollen, besteht darin, dass das Segment mit schreibgeschützten Daten im Arbeitsspeicher zwischen allen von der ausführbaren Datei gestarteten Prozessen gemeinsam genutzt werden kann. Das befreit offensichtlich etwas RAM davon, verschwendet zu werden, um redundante Kopien der gleichen Information zu behalten.

    
Dummy00001 19.06.2010 16:08
quelle
1

Ich bin nicht sicher, was C / C ++ - Standards für Strings bedeuten. Aber ich kann genau sagen, was tatsächlich mit String-Literalen in MSVC passiert. Und ich glaube, andere Compiler verhalten sich ähnlich.

String-Literale befinden sich in einem const-Datenabschnitt. Ihr Speicher wird in den Prozessadressraum abgebildet. Die Speicherseiten, in denen sie gespeichert sind, sind jedoch nur ead-only (sofern nicht explizit während des Laufs geändert).

Aber es gibt noch etwas, was du wissen solltest. Nicht alle C / C ++ - Ausdrücke, die Anführungszeichen enthalten, haben dieselbe Bedeutung. Lass uns alles klären.

%Vor%

Die obige Anweisung bewirkt, dass der Compiler ein Zeichenfolgenliteral "test" erstellt. Der Linker stellt sicher, dass er in der ausführbaren Datei enthalten ist. Der Compiler erzeugt im Funktionskörper einen Code, der auf dem Stack eine Variable a deklariert, die durch die Adresse des String-Literals "test." Initialisiert wird.

%Vor%

Hier deklarieren Sie eine weitere Variable b auf dem Stack, die den Wert von a erhält. Da a auf eine schreibgeschützte Adresse zeigt, würde dies auch b bedeuten. Die gerade Tatsache b hat keine const Semantik bedeutet nicht, dass Sie ändern können, worauf sie zeigt.

%Vor%

Das obige erzeugt eine Zugriffsverletzung. Das Fehlen von const bedeutet wiederum nichts auf der Maschinenebene

%Vor%

Zuallererst - das obige bezieht sich nicht auf String-Literale. 'a' ist keine Zeichenkette. Es ist ein Charakter. Es ist nur eine Nummer. Es ist wie das Schreiben von folgendem:

%Vor%

Der obige Code macht den Compiler zum Narren. Sie sagen, dass die Variable const ist, aber Sie können es ändern. Dies hängt jedoch nicht mit der Prozessorausnahme zusammen, da sich d dennoch im Lese- / Schreibspeicher befindet.

Ich möchte noch einen weiteren Fall hinzufügen:

%Vor%

Das obige deklariert ein Array auf dem Stapel und initialisiert es mit der Zeichenfolge "test". Es befindet sich im Lese- / Schreibspeicher und kann geändert werden. Das ist kein Problem.

    
valdo 19.06.2010 17:28
quelle
0
  

Ich habe keine Warnungen selbst bei den meisten Compiler-Flags

Wirklich? Wenn ich das folgende Code-Snippet kompiliere:

%Vor%

auf g ++ 4.5.0 auch ohne Flags , bekomme ich die folgende Warnung:

  

Warnung: veraltete Konvertierung von String-Konstante in 'char *'

    
fredoverflow 19.06.2010 09:49
quelle
0

Sie können in c schreiben, weil Sie es nicht const gemacht haben. Die Definition von c als const wäre korrekt, da die rechte Seite den Typ const char* hat.

Er erzeugt zur Laufzeit einen Fehler, da der "test" -Wert wahrscheinlich dem schreibgeschützten Codesegment zugeordnet ist. Siehe hier und hier .

    
Mau 19.06.2010 09:50
quelle

Tags und Links