Ich interessiere mich dafür, wo String-Literale zugewiesen / gespeichert werden.
Ich habe hier eine faszinierende Antwort gefunden :
Das Definieren einer Zeichenfolge inline bettet die Daten tatsächlich in das Programm selbst ein und kann nicht geändert werden (einige Compiler erlauben dies durch einen intelligenten Trick, stören Sie sich nicht).
Aber es hatte mit C ++ zu tun, ganz zu schweigen davon, dass es heißt, sich nicht darum zu kümmern.
Ich störe. = D.
Meine Frage ist also, wo und wie mein String-Literal aufbewahrt wird. Warum sollte ich nicht versuchen, es zu ändern? Variiert die Implementierung je nach Plattform? Möchte jemand den "intelligenten Trick" näher erläutern?
quelle
foo = "hello"
in diesem Fall), kann dies zu unbeabsichtigten Nebenwirkungen führen ... (vorausgesetzt, Sie sind nicht erneut Speicher mitnew
oder etwaschar *p = "abc";
, um veränderbare Zeichenfolgen zu erstellen, wie von @ChrisCooperDarauf gibt es keine Antwort. Die C- und C ++ - Standards besagen lediglich, dass Zeichenfolgenliterale eine statische Speicherdauer haben. Jeder Versuch, sie zu ändern, führt zu einem undefinierten Verhalten, und mehrere Zeichenfolgenliterale mit demselben Inhalt können denselben Speicher gemeinsam nutzen oder nicht.
Abhängig vom System, für das Sie schreiben, und den Funktionen des verwendeten ausführbaren Dateiformats werden sie möglicherweise zusammen mit dem Programmcode im Textsegment gespeichert oder verfügen über ein separates Segment für initialisierte Daten.
Die Ermittlung der Details hängt auch von der Plattform ab. Höchstwahrscheinlich sind Tools enthalten, mit denen Sie feststellen können, wo sie sich befinden. Einige geben Ihnen sogar die Kontrolle über solche Details, wenn Sie dies möchten (z. B. können Sie mit gnu ld ein Skript bereitstellen, in dem Sie alles über das Gruppieren von Daten, Code usw. erfahren).
quelle
movb $65, 8(%esp); movb $66, 9(%esp); movb $0, 10(%esp)
für die Zeichenfolge generiert"AB"
, aber die meiste Zeit befindet er sich in einem Nicht-Code-Segment wie.data
oder.rodata
oder ähnlichem (abhängig davon, ob das Ziel unterstützt oder nicht) schreibgeschützte Segmente).Warum sollte ich nicht versuchen, es zu ändern?
Weil es undefiniertes Verhalten ist. Zitat aus C99 N1256 Entwurf 6.7.8 / 32 "Initialisierung" :
Wohin gehen sie?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: Stapelchar *s
::.rodata
Abschnitt der Objektdatei.text
Abschnitt der Objektdatei ausgegeben wird, der über Lese- und Ausführungsberechtigungen verfügt, jedoch nicht über SchreibberechtigungenProgramm:
Kompilieren und dekompilieren:
Die Ausgabe enthält:
Der String wird also im gespeichert
.rodata
Abschnitt .Dann:
Enthält (vereinfacht):
Dies bedeutet, dass das Standard-Linker-Skript sowohl
.text
als auch.rodata
in ein Segment kopiert, das ausgeführt, aber nicht geändert werden kann (Flags = R E
). Der Versuch, ein solches Segment zu ändern, führt unter Linux zu einem Segfault.Wenn wir dasselbe tun für
char[]
:wir erhalten:
so wird es im Stapel gespeichert (relativ zu
%rbp
), und wir können es natürlich ändern.quelle
Zu Ihrer Information, nur die anderen Antworten sichern:
Die Norm: ISO / IEC 14882: 2003 lautet:
quelle
gcc erstellt einen
.rodata
Abschnitt, der "irgendwo" im Adressraum zugeordnet und als schreibgeschützt markiert wird.Visual C ++ (
cl.exe
) erstellt einen.rdata
Abschnitt für denselben Zweck.Sie können sich die Ausgabe von
dumpbin
oderobjdump
(unter Linux) ansehen, um die Abschnitte Ihrer ausführbaren Datei anzuzeigen.Z.B
quelle
printf("some null terminated static string");
anstelle vonprintf(*address);
in C kennen)Dies hängt vom Format Ihrer ausführbaren Datei ab . Eine Möglichkeit, darüber nachzudenken, besteht darin, dass Sie bei der Assembly-Programmierung möglicherweise Zeichenfolgenliterale in das Datensegment Ihres Assembly-Programms einfügen. Ihr C-Compiler macht so etwas, aber alles hängt davon ab, für welches System Ihre Binärdatei kompiliert wird.
quelle
String-Literale werden häufig dem Nur-Lese-Speicher zugewiesen, wodurch sie unveränderlich werden. Bei einigen Compilern ist eine Änderung jedoch durch einen "intelligenten Trick" möglich. Und der intelligente Trick besteht darin, "einen auf den Speicher zeigenden Zeichenzeiger zu verwenden". Denken Sie daran, dass einige Compiler dies möglicherweise nicht zulassen. Hier ist eine Demo
quelle
Da dies von Compiler zu Compiler unterschiedlich sein kann, besteht der beste Weg darin, einen Objektspeicherauszug nach dem gesuchten Zeichenfolgenliteral zu filtern:
Dabei wird
-s
erzwungenobjdump
, den vollständigen Inhalt aller Abschnitte anzuzeigen. Diesmain.o
ist die Objektdatei,-B 1
erzwingtgrep
das Drucken einer Zeile vor dem Abgleich (damit Sie den Abschnittsnamen sehen können) undstr
ist das gesuchte Zeichenfolgenliteral.Mit gcc auf einem Windows-Computer und einer in
main
like deklarierten VariablenLaufen
kehrt zurück
quelle