Stringliterale: Wohin gehen sie?
-
25-09-2019 - |
Frage
Ich bin an dem Stringliterale zugewiesen bekommen / gespeichert.
habe ich eine interessante Antwort hier und sagt:
Inline einen String definieren tatsächlich die Daten in das Programm bettet sich und kann nicht geändert werden (einige Compiler ermöglichen dies durch einen intelligenten Trick, nicht stören).
Aber, es hatte mit C ++ zu tun, nicht zu erwähnen, dass es sagt, nicht zu stören.
Ich stört. = D
Also meine Frage ist, wo und wie ist mein Stringliteral gehalten? Warum sollte ich nicht versuchen, es zu ändern? Variiert die Implementierung von Plattform? Hat jemand Sorgfalt auf dem erarbeiten „smart Trick?“
Lösung
Eine übliche Technik ist für Stringliterale in „read-only-Daten“ gesetzt werden, die in den Prozessraum als Nur-Lese-abgebildet wird (weshalb man es nicht ändern kann).
Es ist je nach Plattform variieren. Zum Beispiel einfache Chip-Architekturen können nicht schreibgeschützt Speichersegmente unterstützen, so wird das Datensegment beschreibbar sein.
versuchen vielmehr dann, um herauszufinden, einen Trick, um Stringliterale wechselbar (es wird in hohem Maße abhängig von den verwendeten Plattform und könnte Zeit ändern) zu machen, nur Arrays verwenden:
char foo[] = "...";
Der Compiler wird für die Array anordnen aus dem buchstäblichen initialisiert werden, und Sie können das Array ändern.
Andere Tipps
Es gibt keine Antwort auf diese Frage. Die C- und C ++ Standards nur sagen, dass Stringliterale statische Speicherdauer hat, jeder Versuch, sie zu modifizieren undefinierte Verhalten gibt, und mehr Zeichenkette mit dem gleichen Inhalt kann oder auch nicht die gleichen Speicher teilen.
Je nach System Sie schreiben für, und die Fähigkeiten des ausführbaren Dateiformat nutzt, können sie mit dem Programmcode in dem Textsegment gespeichert entlang werden, oder sie können ein eigenes Segment für initialisierte Daten.
die Details Bestimmung variiert je nach Plattform und - höchstwahrscheinlich gehören Werkzeuge, die Sie sagen, wo es sie setzen wird. Einige werden sogar geben Sie Details Steuerung über so, wenn Sie es wollen (zum Beispiel Gnu ld ermöglicht es Ihnen, ein Skript zu liefern, es zu sagen, alles darüber, wie Sie Gruppendaten, Code, etc.)
Warum sollte ich nicht versuchen, es zu ändern?
Da ist es nicht definiertes Verhalten. Zitat von C99 N1256 Entwurf 6.7 0,8 / 32 "Initialisierung" :
Beispiel 8: Die Deklaration
char s[] = "abc", t[3] = "abc";
Definiert "plain" char Array-Objekte
s
t
und deren Elemente mit Zeichenfolge Literale initialisiert.Diese Erklärung ist identisch mit
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
Der Inhalt der Arrays ist modifizierbar. Auf der anderen Seite, die Deklaration
char *p = "abc";
Definiert
p
mit Typ „Zeiger auf char“ und initialisiert sie, um Punkt zu einem Objekt mit Typ „array of char“ mit der Länge 4, deren Elemente mit einem Zeichenkettenliteral initialisiert. Wenn ein Versuch, die Verwendungp
gemacht wird, den Inhalt des Arrays zu ändern, wird das Verhalten nicht definiert.
Wohin gehen sie?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
-
char s[]
: stack -
char *s
:-
.rodata
Abschnitt der Objektdatei - das gleiche Segment, in dem der
.text
Abschnitt der Objektdatei gespeichert wird, die lesen und Exec-Berechtigungen verfügen, aber nicht schreiben
-
Programm:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Übersetzen und decompile:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Ausgabe enthält:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
So die Zeichenfolge im .rodata
Abschnitt gespeichert werden.
Dann:
readelf -l a.out
Enthält (vereinfacht):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000704 0x0000000000000704 R E 200000
Section to Segment mapping:
Segment Sections...
02 .text .rodata
Das bedeutet, dass die Standard-Linker-Skript-Dumps sowohl .text
und .rodata
in ein Segment, das ausgeführt werden kann, aber nicht verändert werden (Flags = R E
). Der Versuch, so zu modifizieren, um ein Segment führt zu einem segfault in Linux.
Wenn wir das gleiche für char[]
tun:
char s[] = "abc";
erhalten wir:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
, so dass es in dem Stapel (bezogen auf %rbp
) gespeichert wird, und wir können es natürlich ändern.
FYI, nur die anderen Antworten zu sichern:
Der Standard: ISO / IEC 14882: 2003 sagt:
2,13. Stringliterale
[...] Ein gewöhnlicher Stringliteral hat Typ „Array von
n const char
“ und statische Speicherdauer (3.7)Ob alle Stringliterale verschieden sind (die sind ist gespeichert in nicht überlappenden Objekten) wird implementierungs definiert. Der Effekt von Versuch, eine Stringliteral zu modifizieren nicht definiert ist.
gcc macht einen .rodata
Abschnitt, der „irgendwo“ in Adressraum abgebildet wird und markierte nur gelesen wird,
Visual C ++ (cl.exe
) macht einen .rdata
Abschnitt für den gleichen Zweck.
Sie können die Ausgabe von dumpbin
oder objdump
sehen (unter Linux), um die Abschnitte Ihrer ausführbaren Datei zu sehen.
z.
>dumpbin vec1.exe
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file vec1.exe
File Type: EXECUTABLE IMAGE
Summary
4000 .data
5000 .rdata <-- here are strings and other read-only stuff.
14000 .text
Es hängt von dem Format Ihre ausführbare . Eine Möglichkeit, darüber nachzudenken ist, dass, wenn Sie Montage wurden Programmierung, können Sie Stringliterale im Datensegment der Assembly-Programm setzen. Ihr C-Compiler tut so etwas, aber es hängt alles davon ab, welches System Sie sind binär für kompiliert wird.
Stringliterale werden häufig mit dem Nur-Lese-Speicher zugewiesen, so dass sie unveränderlich zu machen. Allerdings ist in einigen Compilern Modifikation möglich durch einen „intelligenten Trick“ .. Und der Smart Trick ist durch „mit Zeichenzeiger zeigt auf Speicher“ .. erinnern einige Compiler kann nicht zulassen, dass this..Here ist Demo
char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // Displays "Lound"
Da dies von Compiler zu Compiler unterscheiden könnte, ist der beste Weg ist, um ein Objekt Dump-Filter für die Stringliteral gesucht:
objdump -s main.o | grep -B 1 str
wo -s
Kräfte objdump
den gesamten Inhalt aller Abschnitte angezeigt wird, ist main.o
die Objektdatei, -B 1
Kräfte grep
auch vor dem Spiel eine Zeile drucken (so dass Sie die Abschnittsnamen sehen) und str
ist die Stringliteral ‚re der Suche nach.
Mit gcc auf einem Windows-Rechner, und eine Variable deklariert in main
wie
char *c = "whatever";
läuft
objdump -s main.o | grep -B 1 whatever
Rückkehr
Contents of section .rdata:
0000 77686174 65766572 00000000 whatever....