Historisch (vielleicht durch Umschreiben von Teilen) war es das Gegenteil. Auf den allerersten Computern der frühen 1970er Jahre (möglicherweise PDP-11 ), auf denen ein prototypisches embryonales C (möglicherweise BCPL ) ausgeführt wurde, gab es keine MMU und keinen Speicherschutz (der auf den meisten älteren IBM / 360- Mainframes vorhanden war). Also jeder Byte Speicher (einschließlich der Handhabung Literalzeichenfolgen oder Maschinencode) könnte durch ein fehlerhaftes Programm überschrieben werden (man stelle mir ein Programm einige Wechsel %
zu /
in einem printf (3) Format - String). Daher waren wörtliche Zeichenfolgen und Konstanten beschreibbar.
Als Teenager habe ich 1975 im Museum Palais de la Découverte in Paris auf Computern ohne Speicherschutz aus den alten 1960er Jahren codiert: IBM / 1620 verfügte nur über einen Kernspeicher, den Sie über die Tastatur initialisieren konnten, sodass Sie mehrere Dutzend eingeben mussten von Ziffern zum Lesen des Anfangsprogramms auf Lochbändern; CAB / 500 hatte einen magnetischen Trommelspeicher; Sie könnten das Schreiben einiger Spuren durch mechanische Schalter in der Nähe der Trommel deaktivieren.
Später erhielten Computer eine Speicherverwaltungseinheit (Memory Management Unit, MMU) mit einem gewissen Speicherschutz. Es gab ein Gerät, das der CPU verbot, eine Art Speicher zu überschreiben. Einige Speichersegmente, insbesondere das Codesegment (auch bekannt als .text
Segment), waren schreibgeschützt (mit Ausnahme des Betriebssystems, das sie von der Festplatte geladen hat). Es war für den Compiler und den Linker selbstverständlich, die Literalzeichenfolgen in dieses Codesegment einzufügen, und Literalzeichenfolgen wurden schreibgeschützt. Als Ihr Programm versuchte, sie zu überschreiben, war dies ein undefiniertes Verhalten . Ein schreibgeschütztes Codesegment im virtuellen Speicher bietet einen erheblichen Vorteil: Mehrere Prozesse, auf denen dasselbe Programm ausgeführt wird, teilen sich denselben RAM ( physischen Speicher)Seiten) für dieses Codesegment (siehe MAP_SHARED
Flag für mmap (2) unter Linux).
Heutzutage haben billige Mikrocontroller einen Nur-Lese-Speicher (z. B. Flash oder ROM) und behalten dort ihren Code (und die wörtlichen Zeichenfolgen und anderen Konstanten). Reale Mikroprozessoren (wie der in Ihrem Tablet, Laptop oder Desktop) verfügen über eine ausgeklügelte Speicherverwaltungseinheit und Cache- Maschinen, die für virtuellen Speicher und Paging verwendet werden . Das Codesegment des ausführbaren Programms (z. B. in ELF ) ist also ein Speicher, der als schreibgeschütztes, gemeinsam nutzbares und ausführbares Segment abgebildet wird (gemäß mmap (2) oder execve (2) unter Linux; übrigens können Sie ld Anweisungen gebenum ein beschreibbares Codesegment zu erhalten, wenn Sie es wirklich wollten). Das Schreiben oder Missbrauchen ist im Allgemeinen ein Segmentierungsfehler .
Der C-Standard ist also barock: Literal-Strings sind legal (nur aus historischen Gründen) keine const char[]
Arrays, sondern nur char[]
Arrays, deren Überschreiben verboten ist.
Übrigens erlauben nur wenige aktuelle Sprachen das Überschreiben von Zeichenfolgenliteralen (selbst Ocaml, das historisch - und schlecht - beschreibbare Zeichenfolgen hatte, hat dieses Verhalten kürzlich in 4.02 geändert und enthält jetzt schreibgeschützte Zeichenfolgen).
Aktuelle C - Compiler sind in der Lage zu optimieren und haben "ions"
und "expressions"
ihre letzten 5 Bytes (einschließlich des abschließenden Null - Byte) teilen.
Versuchen Sie, Ihren C-Code in der Datei foo.c
mit zu kompilieren gcc -O -fverbose-asm -S foo.c
und in der foo.s
von GCC generierten Assembler-Datei nachzuschauen
Schließlich ist die Semantik von C komplex genug (lesen Sie mehr über CompCert & Frama-C, die versuchen, sie zu erfassen), und das Hinzufügen von beschreibbaren Konstanten-Literal-Strings würde sie noch arkaner machen, während Programme schwächer und noch weniger sicher (und mit weniger Sicherheit) werden Daher ist es sehr unwahrscheinlich, dass zukünftige C-Standards beschreibbare wörtliche Zeichenfolgen akzeptieren. Vielleicht würden sie sie im Gegenteil zu const char[]
Arrays machen, wie sie moralisch sein sollten.
Beachten Sie auch, dass es aus vielen Gründen für den Computer schwieriger ist, veränderbare Daten zu handhaben (Cache-Kohärenz), für die der Entwickler Code schreiben kann, als für konstante Daten. Daher ist es vorzuziehen, dass die meisten Ihrer Daten (und insbesondere die wörtlichen Zeichenfolgen) unveränderlich bleiben . Lesen Sie mehr über funktionale Programmierung Paradigma .
In den alten Fortran77 Tage auf IBM / 7094 könnte ein Fehler sogar eine Konstante ändern: Wenn Sie CALL FOO(1)
und wenn FOO
passiert sein Argument durch Bezugnahme auf 2 geführt zu ändern, haben die Durchführung andere Vorkommen von 1 in 2 geändert könnten, und das war ein wirklich ungezogener Bug, ziemlich schwer zu finden.