Perché non dovrei provare a modificarlo?
Perché è un comportamento indefinito. Citazione da C99 N1256 bozza 6.7.8 / 32 "Inizializzazione" :
ESEMPIO 8: La dichiarazione
char s[] = "abc", t[3] = "abc";
definisce oggetti "semplici" di array di caratteri s
e i t
cui elementi sono inizializzati con valori letterali di stringa di caratteri.
Questa dichiarazione è identica a
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Il contenuto degli array è modificabile. D'altra parte, la dichiarazione
char *p = "abc";
definisce p
con il tipo "pointer to char" e lo inizializza in modo che punti a un oggetto con tipo "array of char" con lunghezza 4 i cui elementi sono inizializzati con una stringa di caratteri letterale. Se si tenta di utilizzare p
per modificare il contenuto dell'array, il comportamento non è definito.
Dove vanno?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: stack
char *s
:
.rodata
sezione del file oggetto
- lo stesso segmento in cui
.text
viene scaricata la sezione del file oggetto, che dispone delle autorizzazioni di lettura ed Exec, ma non di scrittura
Programma:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Compilare e decompilare:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
L'output contiene:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Quindi la stringa è memorizzata nella .rodata
sezione.
Poi:
readelf -l a.out
Contiene (semplificato):
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
Ciò significa che lo script del linker predefinito esegue il dump di entrambi .text
e .rodata
in un segmento che può essere eseguito ma non modificato ( Flags = R E
). Il tentativo di modificare un tale segmento porta a un segfault in Linux.
Se facciamo lo stesso per char[]
:
char s[] = "abc";
otteniamo:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
quindi viene memorizzato nello stack (rispetto a %rbp
) e ovviamente possiamo modificarlo.