Я заметил, что строковые литералы имеют совсем другие адреса в памяти, чем другие константы и переменные (Linux ОС): у них есть многие, продвижение обнуляет (не распечатанный).
Пример:
const char *h = "Hi";
int i = 1;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) &i);
Вывод:
0x400634
0x7fffc1ef1a4c
Я знаю, что они хранятся в .rodata
часть исполняемого файла. Существует ли специальный способ, которым ОС обрабатывает его впоследствии, таким образом, литералы заканчиваются в специальной области памяти (с продвижением, обнуляет)? Есть ли какие-либо преимущества той ячейки памяти или есть ли что-то специальное об этом?
Вот то, как память процесса размечается на Linux (от http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/ ):
раздел .rodata является подразделом защищенным от записи Инициализированные Глобальные Данные блок. (Раздел, который ELF исполняемые файлы определяют .data, является своим перезаписываемым дубликатом для перезаписываемого globals, инициализированного к ненулевым значениям. Перезаписываемые globals, инициализированные к нулям, переходят в блок .bss . globals здесь я имею в виду глобальные переменные и весь статичный переменные независимо от размещения.)
изображение должно объяснить численные значения Ваших адресов.
, Если Вы хотите заняться расследованиями далее, затем на Linux, можно осмотреть /proc/$pid/maps виртуальные файлы, которые описывают расположение памяти выполнения процессов. Вы не получите зарезервированное (запускающийся с точки) имена раздела ELF, но можно предположить, какой раздел ELF блок памяти, порожденный из путем рассмотрения его защиты памяти, отмечает. Например, выполнение
$ cat /proc/self/maps #cat's memory map
дает мне
00400000-0040b000 r-xp 00000000 fc:00 395465 /bin/cat
0060a000-0060b000 r--p 0000a000 fc:00 395465 /bin/cat
0060b000-0060d000 rw-p 0000b000 fc:00 395465 /bin/cat
006e3000-00704000 rw-p 00000000 00:00 0 [heap]
3000000000-3000023000 r-xp 00000000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000222000-3000223000 r--p 00022000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000223000-3000224000 rw-p 00023000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000224000-3000225000 rw-p 00000000 00:00 0
3000400000-30005ba000 r-xp 00000000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30005ba000-30007ba000 ---p 001ba000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007ba000-30007be000 r--p 001ba000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007be000-30007c0000 rw-p 001be000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007c0000-30007c5000 rw-p 00000000 00:00 0
7f49eda93000-7f49edd79000 r--p 00000000 fc:00 2104890 /usr/lib/locale/locale-archive
7f49edd79000-7f49edd7c000 rw-p 00000000 00:00 0
7f49edda7000-7f49edda9000 rw-p 00000000 00:00 0
7ffdae393000-7ffdae3b5000 rw-p 00000000 00:00 0 [stack]
7ffdae3e6000-7ffdae3e8000 r--p 00000000 00:00 0 [vvar]
7ffdae3e8000-7ffdae3ea000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
первое r-xp
, блок определенно прибыл от [1 126] (исполняемый код) .text , первое r--p
блок от [1 127] .rodata и следующий rw - блоки от [1 128] .bss и .data. (Промежуточный "куча" и блок стека являются блоками, загруженными из динамически подключаемых библиотек динамическим компоновщиком.)
Примечание: Для исполнения стандарта необходимо бросить int*
для "%p"
к (void*)
, или иначе поведение не определено.
Поэтому строковые литералы имеют статическая продолжительность хранения . Таким образом, они будут жить во время целой программы. Такие переменные могут быть сохранены в специальной ячейке памяти, которая не является ни на так называемой "куче", ни на стеке. Следовательно различие в адресах.
Помните, что то, где указатель , отличается от где указатель точки к . Более реалистическое (от яблок к яблокам), которыми сравнение было бы
printf ("%p\n", (void *) &h);
printf ("%p\n", (void *) &i);
, я подозреваю, что Вы найдете, что h
и p
имеют подобные адреса. Или, другой более - реалистическое сравнение было бы
static int si = 123;
int *ip = &si;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) ip);
, я подозреваю, что Вы найдете что h
и ip
точка к подобному региону памяти.
Полагайте, что литералы являются переменными только для чтения и также, существует понятие литерального пула. То, что литеральный пул, является набором уникальных литералов программы, где дублирующиеся константы отбрасываются, поскольку ссылки объединяются в одну.
существует один литеральный пул для каждого источника, и в зависимости от изощренности ссылки / связывают программу, литеральные пулы могут быть помещены друг рядом с другом для создания одного .rodata.
нет также никакой гарантии, что литеральный пул только для чтения защищенный. Язык, хотя проекты компилятора рассматривают его как так.
Рассматривают мой фрагмент кода. Я мог иметь
символ константы *CP = "привет мир";
символ константы *cp1 = "привет мир";
хороший компилятор распознает, что в том исходном коде, литералы только для чтения CP, cp1, указывает на идентичные строки и выскажет cp1 мнение к литералу CP, отбрасывая второй.
Еще одна точка. Литеральный пул может быть кратным 256-байтовым или другому значению. Если данные пула составят меньше чем 256 байтов, то слабое будет дополнено шестнадцатеричными нулями.
Различные компиляторы, следуйте общим стандартам разработки, разрешая модуль, скомпилированный с [1 112] C, чтобы быть связанными с модулем, скомпилированным с [1 113] ассемблер или другой язык. Два литеральных пула помещаются последовательно в .rodata.
printf ("%p\n", h); // h is the address of "Hi", which is in the rodata or other segments of the application.
printf ("%p\n", &i); // I think "i" is not a global variable, so &i is in the stack of main. The stack address is by convention in the top area of the memory space of the process.