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


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

пример:

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

выход:

0x400634
0x7fffc1ef1a4c

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

5 62

5 ответов:

вот как процесс памяти выкладывается на Linux (от http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/):

Linux process memory layout

The .rodata раздел является защищенным от записи подразделом Инициализированные Глобальные Данные блок. (Раздел, который эльф исполняемые файлы, назначить .данные является его коллегой для записи для записи глобальные переменные инициализируются к нулю ценности. Записываемые глобальные переменные инициализируются нулями, чтобы перейти к .БСС блок. Под глобалами здесь я подразумеваю глобальные переменные и все статический переменные независимо от размещения.)

изображение должно объяснить числовые значения ваших адресов.

если вы хотите исследовать дальше, то на Линуксе вы можете проверить /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 блок определенно пришел из .текст (исполняемый код), первый r--p блок .rodata, а после rw-- блоки .БСС и .данные. (В между кучей и блоком стека находятся блоки, загруженные из динамически связанных библиотек динамическим компоновщиком.)


Примечание: для того чтобы исполнить с стандартом, вы должны бросить 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 укажите на аналогичную область памяти.

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

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

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

рассмотрим мой фрагмент кода. Я мог бы

const char *cp= "hello world";
const char * cp1= "hello world";

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

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

различные компиляторы, следуют общим стандартам разработки, позволяя модуль компилируется с C, чтобы быть связанным с модулем, скомпилированным с язык ассемблера или другой язык. Два литеральных пула помещаются последовательно .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.