C99 N1256 Entwurf
Es gibt zwei verschiedene Verwendungen von Zeichenkettenliteralen:
Initialisieren char[]
:
char c[] = "abc";
Dies ist "mehr Magie" und wird unter 6.7.8 / 14 "Initialisierung" beschrieben:
Ein Array vom Zeichentyp kann durch ein Zeichenfolgenliteral initialisiert werden, das optional in geschweiften Klammern eingeschlossen ist. Aufeinanderfolgende Zeichen des Zeichenfolgenliteral (einschließlich des abschließenden Nullzeichens, wenn Platz vorhanden ist oder wenn das Array eine unbekannte Größe hat) initialisieren die Elemente des Arrays.
Dies ist also nur eine Abkürzung für:
char c[] = {'a', 'b', 'c', '\0'};
c
Kann wie jedes andere reguläre Array geändert werden.
Überall sonst: es erzeugt ein:
Also, wenn Sie schreiben:
char *c = "abc";
Dies ist ähnlich wie:
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Beachten Sie die implizite Besetzung von char[]
bis char *
, die immer legal ist.
Wenn Sie dann ändern c[0]
, ändern Sie auch __unnamed
, was UB ist.
Dies ist unter 6.4.5 "String-Literale" dokumentiert:
5 In der Übersetzungsphase 7 wird an jede Multibyte-Zeichenfolge, die sich aus einem String-Literal oder Literalen ergibt, ein Byte oder ein Code mit dem Wert Null angehängt. Die Multibyte-Zeichenfolge wird dann verwendet, um ein Array mit statischer Speicherdauer und -länge zu initialisieren, das gerade ausreicht, um die Folge aufzunehmen. Bei Zeichenfolgenliteralen haben die Array-Elemente den Typ char und werden mit den einzelnen Bytes der Multibyte-Zeichenfolge [...] initialisiert.
6 Es ist nicht spezifiziert, ob diese Arrays unterschiedlich sind, vorausgesetzt, ihre Elemente haben die entsprechenden Werte. Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.
6.7.8 / 32 "Initialisierung" gibt ein direktes Beispiel:
BEISPIEL 8: Die Erklärung
char s[] = "abc", t[3] = "abc";
definiert „plain“ char Array - Objekte s
und t
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 kann geändert werden. Auf der anderen Seite die Erklärung
char *p = "abc";
definiert p
mit dem Typ "Zeiger auf Zeichen" und initialisiert es so, dass es auf ein Objekt mit dem Typ "Array von Zeichen" mit der Länge 4 zeigt, dessen Elemente mit einem Zeichenfolgenliteral initialisiert werden. Wenn versucht wird, p
den Inhalt des Arrays zu ändern, ist das Verhalten undefiniert.
GCC 4.8 x86-64 ELF-Implementierung
Programm:
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Kompilieren und dekompilieren:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Die 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
Fazit: GCC speichert char*
es in .rodata
Abschnitt, nicht in .text
.
Beachten Sie jedoch , dass der Standard Linker - Skript setzt .rodata
und .text
im gleichen Segment , das auszuführen hat , aber keine Schreibrechte. Dies kann beobachtet werden mit:
readelf -l a.out
was beinhaltet:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
Wenn wir dasselbe tun für char[]
:
char s[] = "abc";
wir erhalten:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
so wird es im Stapel gespeichert (relativ zu %rbp
).