Какой адрес печатается функцией printf() в формате %p в c?

У меня есть простой код следующим образом:

#include<stdio.h>

int glob;

int main(void)
{
   int a;
   printf("&a is : %p \n", &a);
   printf("glob is : %p \n", &glob);
   return 0;
}

Вывод вышеуказанной программы: Первый запуск:

&a is : 0x7fff70de91ec
glob is : 0x6008f4

Второй запуск:

&a is : 0x7fff38c4c7ac
glob is : 0x6008f4

Я изучаю виртуальные и физические адреса. У меня следующий вопрос:

  1. Какой напечатанный адрес (физический/виртуальный) переменной "a"?
  2. Если он виртуальный, то как он меняется при каждом запуске одной и той же программы? Как я понял, компилятор предоставляет виртуальный адрес переменным во время компиляции?
  3. Почему адрес глобальной переменной постоянен при каждом запуске программы?

Выполнил эту программу в Linux: 2.6.18-308.el5 x86_64 GNU/Linux

Скомпилировано с использованием: gcc версии 4.1.2 20080704 (Red Hat 4.1.2-52)


person BSalunke    schedule 05.04.2013    source источник
comment
Ваша программа вызывает неопределенное поведение. %p должен быть указан указатель на пустоту, поэтому вы должны привести к (void*) в обоих printfs.   -  person Jens    schedule 05.04.2013
comment
@Jens Разве аргумент не будет неявно приведен к void *?   -  person Vilhelm Gray    schedule 05.04.2013
comment
Неявные преобразования @VilhelmGray происходят, когда ожидается тип, но в функциях с переменным числом типов нет.   -  person effeffe    schedule 05.04.2013


Ответы (4)


Адреса, видимые в программе, всегда являются виртуальными, и поведение, описанное в OP, является мерой противодействия Linux, позволяющей избежать переполнения буфера. атаки.

Просто чтобы попробовать, вы можете отключить его с помощью

sysctl -w kernel.randomize_va_space=0

затем снова запустите вашу программу и посмотрите.

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

person Davide Berra    schedule 05.04.2013

Оба адреса являются виртуальными.

Современные системы используют рандомизацию стека для предотвращения так называемых атак, разрушающих стек, поэтому локальная переменная может менять свое местоположение при каждом запуске. Однако глобальная переменная хранится в исполняемом файле и каждый раз загружается с одним и тем же смещением.

person Some programmer dude    schedule 05.04.2013
comment
Стоит отметить, что некоторые другие операционные системы и некоторые другие дистрибутивы Linux, ориентированные на безопасность, имеют PIE (независимые от позиции исполняемые файлы), и в них адрес глобальной переменной также изменится. - person Art; 05.04.2013
comment
@Joachim, да, я согласен, что локальные переменные хранятся в стеке, но почему говорится, что компилятор присваивает переменным определенный адрес во время компиляции? - person BSalunke; 05.04.2013
comment
@BSalunke Локальные переменные размещаются на определенном смещении от указателя стека при вызове функции. Таким образом, хотя компилятор не дает ему фиксированный адрес, переменные по-прежнему имеют адрес, заданный компилятором. - person Some programmer dude; 05.04.2013
comment
@BSalunke Каждая переменная должна сопоставляться с адресом, как еще компилятор должен генерировать инструкции для доступа к ней? В некоторых случаях это могут быть абсолютные адреса (в виртуальном пространстве процесса), как для глобальных, так и для локальных адресов, как правило, относительно стека. - person unwind; 05.04.2013

Ваша программа всегда будет видеть только виртуальный адрес.

Реальные адреса доступны только диспетчеру виртуальной памяти в режиме ядра.

Глобал имеет тот же адрес (пока вы не поместите перед ним другие переменные), потому что он создается в сегменте данных.

Локальные переменные всегда создаются в стеке.

person rkosegi    schedule 05.04.2013
comment
Локальные переменные тоже могут быть static... :) - person unwind; 05.04.2013
comment
@unwind: да, вы правы. Я просто описываю код, который предоставляет OP. - person rkosegi; 05.04.2013

Все адреса, которые видит программа, являются виртуальными. Однако локальные переменные помещаются в стек, а глобальные — в специальную область, называемую сегментом данных. Хотя относительное расположение переменных определяется при компиляции, стек может меняться при каждом запуске.

person Ulka Vaze    schedule 05.04.2013