Pourquoi ne devrais-je pas essayer de le modifier?
Parce que c'est un comportement indéfini. Citation de C99 N1256 draft 6.7.8 / 32 "Initialisation" :
EXEMPLE 8: La déclaration
char s[] = "abc", t[3] = "abc";
définit des objets de tableau de caractères "simples" s
et t
dont les éléments sont initialisés avec des chaînes de caractères littérales.
Cette déclaration est identique à
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Le contenu des tableaux est modifiable. D'autre part, la déclaration
char *p = "abc";
définit p
avec le type "pointer to char" et l'initialise pour pointer vers un objet de type "array of char" de longueur 4 dont les éléments sont initialisés avec une chaîne de caractères littérale. Si une tentative d'utilisation est faite pour p
modifier le contenu du tableau, le comportement n'est pas défini.
Où vont-ils?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: pile
char *s
:
.rodata
section du fichier objet
- le même segment où la
.text
section du fichier objet est vidée, qui a des autorisations de lecture et d'exécution, mais pas d'écriture
Programme:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Compilez et décompilez:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
La sortie contient:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
La chaîne est donc stockée dans la .rodata
section.
Ensuite:
readelf -l a.out
Contient (simplifié):
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
Cela signifie que le script de l'éditeur de liens par défaut vide à la fois .text
et .rodata
dans un segment qui peut être exécuté mais pas modifié ( Flags = R E
). Tenter de modifier un tel segment conduit à un segfault sous Linux.
Si nous faisons de même pour char[]
:
char s[] = "abc";
on obtient:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
il est donc stocké dans la pile (par rapport à %rbp
), et nous pouvons bien sûr le modifier.