Меня интересует, где строковые литералы распределяются / хранятся.
Я нашел один интригующий ответ здесь , говоря:
Определение строки inline фактически встраивает данные в саму программу и не может быть изменено (некоторые компиляторы допускают это умным трюком, не беспокойтесь).
Но это было связано с C ++, не говоря уже о том, что он говорит не беспокоить.
Я беспокоюсь. = D
Итак, мой вопрос: где и как хранится мой строковый литерал? Почему я не должен пытаться изменить это? Реализация зависит от платформы? Кто-нибудь хочет уточнить «умный трюк»?
источник
foo = "hello"
в этом случае) может вызвать непреднамеренные побочные эффекты ... (при условии, что вы не выделения памяти сnew
или что - то)char *p = "abc";
для создания изменяемых строк, как это было сказано @ChrisCooperТам нет ни одного ответа на это. Стандарты C и C ++ просто говорят, что строковые литералы имеют статическую продолжительность хранения, любая попытка их изменения приводит к неопределенному поведению, а несколько строковых литералов с одинаковым содержимым могут или не могут совместно использовать одно и то же хранилище.
В зависимости от системы, для которой вы пишете, и возможностей используемого формата исполняемого файла, они могут храниться вместе с программным кодом в текстовом сегменте или иметь отдельный сегмент для инициализированных данных.
Определение деталей также будет зависеть от платформы - скорее всего, есть инструменты, которые могут подсказать вам, где она находится. Некоторые даже дадут вам контроль над такими деталями, если вы этого хотите (например, gnu ld позволяет вам предоставить скрипт, который расскажет все о том, как группировать данные, код и т. Д.)
источник
movb $65, 8(%esp); movb $66, 9(%esp); movb $0, 10(%esp)
для строки"AB"
, но подавляющее большинство времени, он будет находиться в сегменте не-кода , такие как.data
или.rodata
или т.п. ( в зависимости от наличия или отсутствия целевых опор сегменты только для чтения).Почему я не должен пытаться изменить это?
Потому что это неопределенное поведение. Цитата из проекта C99 N1256 6.7.8 / 32 «Инициализация» :
Куда они идут?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: stackchar *s
:.rodata
раздел объектного файла.text
дамп раздела объекта файла, который имеет разрешения на чтение и исполнение, но не на записьПрограмма:
Компилировать и декомпилировать:
Выход содержит:
Таким образом, строка хранится в
.rodata
разделе.Затем:
Содержит (упрощенно):
Это означает, что скрипт компоновщика по умолчанию создает дамп как для сегмента, так
.text
и.rodata
для сегмента, который может быть выполнен, но не изменен (Flags = R E
). Попытка изменить такой сегмент приводит к ошибке в Linux.Если мы сделаем то же самое для
char[]
:мы получаем:
поэтому он сохраняется в стеке (относительно
%rbp
), и мы, конечно, можем его изменить.источник
К вашему сведению, просто резервное копирование других ответов:
Стандарт: ISO / IEC 14882: 2003 гласит:
источник
gcc создает
.rodata
раздел, который отображается "где-то" в адресном пространстве и помечается только для чтения,Visual C ++ (
cl.exe
) создает.rdata
раздел для той же цели.Вы можете посмотреть на вывод из
dumpbin
илиobjdump
(в Linux), чтобы увидеть разделы вашего исполняемого файла.Например
источник
printf("some null terminated static string");
а неprintf(*address);
в C)Это зависит от формата вашего исполняемого файла . Можно подумать об этом, если бы вы программировали на ассемблере, вы могли бы поместить строковые литералы в сегмент данных вашей программы на ассемблере. Ваш компилятор C делает что-то подобное, но все зависит от того, для какой системы вы бинарный.
источник
Строковые литералы часто размещаются в постоянной памяти, что делает их неизменяемыми. Тем не менее, в некоторых компиляторах модификация возможна с помощью «умного трюка». А умный трюк заключается в «использовании указателя символа, указывающего на память» ... помните, что некоторые компиляторы могут этого не допустить .. Вот демо
источник
Поскольку это может отличаться от компилятора к компилятору, лучший способ - отфильтровать дамп объекта для искомого строкового литерала:
где
-s
заставляетobjdump
отображать полное содержимое всех разделов,main.o
это объектный файл,-B 1
заставляетgrep
также печатать одну строку перед совпадением (чтобы вы могли видеть название раздела) иstr
является строковым литералом, который вы ищете.С gcc на машине с Windows и одной переменной, объявленной
main
какБег
возвращается
источник