Warum sollte ich nicht versuchen, es zu ändern?
Weil es undefiniertes Verhalten ist. Zitat aus C99 N1256 Entwurf 6.7.8 / 32 "Initialisierung" :
BEISPIEL 8: Die Erklärung
char s[] = "abc", t[3] = "abc";
definiert "einfache" char-Array-Objekte s
undt
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.
Wohin gehen sie?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: Stapel
char *s
::
.rodata
Abschnitt der Objektdatei
- Das gleiche Segment, in dem der
.text
Abschnitt der Objektdatei ausgegeben wird, der über Lese- und Ausführungsberechtigungen verfügt, jedoch nicht über Schreibberechtigungen
Programm:
#include <stdio.h>
int main() {
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
Der String wird also im gespeichert .rodata
Abschnitt .
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
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[]
:
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
), und wir können es natürlich ändern.