Почему адреса памяти строковых литералов так отличаются от других на Linux?

Я заметил, что строковые литералы имеют совсем другие адреса в памяти, чем другие константы и переменные (Linux ОС): у них есть многие, продвижение обнуляет (не распечатанный).

Пример:

const char *h = "Hi";
int i = 1;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) &i);

Вывод:

0x400634
0x7fffc1ef1a4c

Я знаю, что они хранятся в .rodata часть исполняемого файла. Существует ли специальный способ, которым ОС обрабатывает его впоследствии, таким образом, литералы заканчиваются в специальной области памяти (с продвижением, обнуляет)? Есть ли какие-либо преимущества той ячейки памяти или есть ли что-то специальное об этом?

62
задан 19 November 2016 в 18:40

5 ответов

Вот то, как память процесса размечается на Linux (от http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/ ):

Linux process 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*), или иначе поведение не определено.

73
ответ дан 31 October 2019 в 14:12

Поэтому строковые литералы имеют статическая продолжительность хранения . Таким образом, они будут жить во время целой программы. Такие переменные могут быть сохранены в специальной ячейке памяти, которая не является ни на так называемой "куче", ни на стеке. Следовательно различие в адресах.

16
ответ дан 31 October 2019 в 14:12

Помните, что то, где указатель , отличается от где указатель точки к . Более реалистическое (от яблок к яблокам), которыми сравнение было бы

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 точка к подобному региону памяти.

7
ответ дан 31 October 2019 в 14:12

Полагайте, что литералы являются переменными только для чтения и также, существует понятие литерального пула. То, что литеральный пул, является набором уникальных литералов программы, где дублирующиеся константы отбрасываются, поскольку ссылки объединяются в одну.

существует один литеральный пул для каждого источника, и в зависимости от изощренности ссылки / связывают программу, литеральные пулы могут быть помещены друг рядом с другом для создания одного .rodata.

нет также никакой гарантии, что литеральный пул только для чтения защищенный. Язык, хотя проекты компилятора рассматривают его как так.

Рассматривают мой фрагмент кода. Я мог иметь

символ константы *CP = "привет мир";
символ константы *cp1 = "привет мир";

хороший компилятор распознает, что в том исходном коде, литералы только для чтения CP, cp1, указывает на идентичные строки и выскажет cp1 мнение к литералу CP, отбрасывая второй.

Еще одна точка. Литеральный пул может быть кратным 256-байтовым или другому значению. Если данные пула составят меньше чем 256 байтов, то слабое будет дополнено шестнадцатеричными нулями.

Различные компиляторы, следуйте общим стандартам разработки, разрешая модуль, скомпилированный с [1 112] C, чтобы быть связанными с модулем, скомпилированным с [1 113] ассемблер или другой язык. Два литеральных пула помещаются последовательно в .rodata.

1
ответ дан 31 October 2019 в 14:12
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.
0
ответ дан 31 October 2019 в 14:12

Другие вопросы по тегам:

Похожие вопросы: