Почему я не должен пытаться изменить это?
Потому что это неопределенное поведение. Цитата из проекта C99 N1256 6.7.8 / 32 «Инициализация» :
ПРИМЕР 8: Декларация
char s[] = "abc", t[3] = "abc";
определяет понятие «простые» объекты массив символов s
и t
чьи элементы инициализируются с символьной строки литералов.
Эта декларация идентична
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Содержимое массивов может быть изменено. С другой стороны, декларация
char *p = "abc";
определяется p
с типом «указатель на символ» и инициализирует его, чтобы указать на объект с типом «массив символа» длиной 4, элементы которого инициализируются литералом символьной строки. Если предпринята попытка использовать p
для изменения содержимого массива, поведение не определено.
Куда они идут?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: stack
char *s
:
.rodata
раздел объектного файла
- тот же сегмент, где
.text
дамп раздела объекта файла, который имеет разрешения на чтение и исполнение, но не на запись
Программа:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Компилировать и декомпилировать:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Выход содержит:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Таким образом, строка хранится в .rodata
разделе.
Затем:
readelf -l a.out
Содержит (упрощенно):
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
Это означает, что скрипт компоновщика по умолчанию создает дамп как для сегмента, так .text
и .rodata
для сегмента, который может быть выполнен, но не изменен ( Flags = R E
). Попытка изменить такой сегмент приводит к ошибке в Linux.
Если мы сделаем то же самое для char[]
:
char s[] = "abc";
мы получаем:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
поэтому он сохраняется в стеке (относительно %rbp
), и мы, конечно, можем его изменить.