Строковые литералы: куда они идут?
меня интересует, где строковые литералы выделяется/хранится.
Я нашел один интригующий ответ здесь, говорят:
определение строки inline фактически встраивает данные в саму программу и не может быть изменено (некоторые компиляторы позволяют это умным трюком, не беспокойтесь).
но, это было связано с C++, не говоря уже о том, что он говорит, чтобы не беспокоиться.
я беспокою. =D
Так что мой вопрос в том, где и как хранится мой строковый литерал? Почему бы мне не попытаться изменить его? Реализация зависит от платформы? Кто-нибудь хочет уточнить "умный трюк"?"
8 ответов:
общий метод заключается в том, что строковые литералы должны быть помещены в раздел "только для чтения", который отображается в пространство процесса как только для чтения (поэтому вы не можете его изменить).
Это зависит от платформы. Например, более простые архитектуры микросхем могут не поддерживать сегменты памяти только для чтения, поэтому сегмент данных будет доступен для записи.
вместо этого попробуйте выяснить трюк, чтобы сделать строковые литералы изменчивыми (это будет сильно зависеть от вашей платформы и может измениться со временем), просто используйте массивы:
char foo[] = "...";компилятор организует инициализацию массива из литерала, и вы можете изменить массив.
на это нет ответа. Стандарты C и C++ просто говорят, что строковые литералы имеют статическую длительность хранения, любая попытка их изменения дает неопределенное поведение, и несколько строковых литералов с одинаковым содержимым могут или не могут совместно использовать одно и то же хранилище.
в зависимости от системы, для которой вы пишете, и возможностей формата исполняемого файла, который он использует, они могут храниться вместе с программным кодом в текстовом сегменте, или они могут иметь отдельный сегмент для инициализированных данных.
определение детали могут варьироваться в зависимости от платформы, а также-скорее всего, включают в себя инструменты, которые могут сказать вам, где он звучит. Некоторые даже дадут вам контроль над такими деталями, если вы этого хотите (например, gnu ld позволяет вам предоставить скрипт, чтобы рассказать все о том, как группировать данные, код и т. д.)
почему бы мне не попытаться изменить его?
потому что это неопределенное поведение. Цитата из С99 проект N12566.7.8/32 "инициализация":
пример 8: объявление
char s[] = "abc", t[3] = "abc";определяет "простые" объекты массива символов
sиtэлементы которого инициализируются символьными строковыми литералами.эта декларация идентична
char s[] = { 'a', 'b', 'c', '' }, t[] = { 'a', 'b', 'c' };содержимое массивов можно изменять. С другой стороны, декларация
char *p = "abc";определяет
pС типом " указатель на char "и инициализирует его, чтобы указать на объект с типом" массив char " длиной 4, элементы которого инициализируются символьным строковым литералом. Если сделана попытка использоватьpчтобы изменить содержимое массива, поведение не определено.откуда они идти?
GCC 4.8 x86-64 Elf Ubuntu 14.04:
char s[]стекchar *s:
- объекта file
- тот же сегмент, где
.textраздел объектного файла сбрасывается, который имеет права чтения и Exec, но не записипрограмма:
#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 x0,-0x8(%rbp) f: 00 c: R_X86_64_32S .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). Попытка изменить такой сегмент приводит к segfault в Linux.если мы сделаем то же самое для
char[]:char s[] = "abc";мы получаем:
17: c7 45 f0 61 62 63 00 movl x636261,-0x10(%rbp)таким образом, он сохраняется в стеке (относительно
%rbp), и мы, конечно, можем изменить его.
FYI, просто резервное копирование других ответов:
стандартные: ISO / IEC 14882: 2003 говорит:
2.13. Строковые литералы
[...]Обычный строковый литерал имеет тип "массив
n const char" и статическую длительность хранения (3.7)различаются ли все строковые литералы (то есть хранятся в непересекающиеся объекты) реализация - определено. Эффект из попытка изменить строковый литерал не определено.
gcc делает
.rodataраздел, который отображается" где-то " в адресном пространстве и помечается только для чтения,Visual C++ (
cl.exe) составляет.rdataРаздел для той же цели.вы можете посмотреть на выход из
dumpbinилиobjdump(в Linux), чтобы увидеть разделы вашего исполняемого файла.например.
>dumpbin vec1.exe Microsoft (R) COFF/PE Dumper Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file vec1.exe File Type: EXECUTABLE IMAGE Summary 4000 .data 5000 .rdata <-- here are strings and other read-only stuff. 14000 .text
строковые литералы часто выделяются в память только для чтения, что делает их неизменяемыми. Однако в некоторых компиляторах модификация возможна с помощью"умного трюка"..И умный трюк заключается в "использовании указателя символа, указывающего на память"..помните, что некоторые компиляторы, возможно, не позволяют этого..Вот демо
char *tabHeader = "Sound"; *tabHeader = 'L'; printf("%s\n",tabHeader); // Displays "Lound"
поскольку это может отличаться от компилятора к компилятору, лучшим способом является фильтрация дампа объекта для искомого строкового литерала:
objdump -s main.o | grep -B 1 strздесь
-sсилobjdumpдля отображения полного содержимого всех разделов,main.oэто объектный файл,-B 1силgrepчтобы также напечатать одну строку перед матчем (чтобы вы могли видеть имя раздела) иstr- это строковый литерал, который вы искали.С gcc на машине Windows и одной переменной объявлено в
mainкакchar *c = "whatever";под управлением
objdump -s main.o | grep -B 1 whateverвозвращает
Contents of section .rdata: 0000 77686174 65766572 00000000 whatever....
Comments