Почему компилятор не знает адреса локальных переменных во время компиляции? - PullRequest
8 голосов
/ 08 февраля 2012

Что означает следующее утверждение?

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

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

Кроме того, не могли бы вы предоставить хорошую ссылку, чтобы прочитать, как распределяются локальные переменные и другие?

Заранее спасибо!

Ответы [ 4 ]

13 голосов
/ 08 февраля 2012

Приведенная выше цитата верна - компилятор обычно не знает адреса локальных переменных во время компиляции.Тем не менее, компилятор, вероятно, знает смещение от основания фрейма стека, в котором будет расположена локальная переменная, но в зависимости от глубины стека вызовов, которое может преобразоваться в другой адрес во время выполнения.В качестве примера рассмотрим этот рекурсивный код (который, кстати, вовсе не является хорошим кодом!):

int Factorial(int num) {
    int result;
    if (num == 0)
        result = 1;
    else
        result = num * Factorial(num - 1);

    return result;
}

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

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

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

Что касается дальнейшего чтения, если вы хотите довольно углубленное рассмотрение того, каккомпилятор может размещать переменные, вы можете выбрать копию Компиляторы: принципы, методы и инструменты, второе издание от Aho, Lam, Sethi и Ullman.Хотя большая часть этой книги касается других методов конструирования компиляторов, большой раздел книги посвящен реализации генерации кода и оптимизации, которая может быть использована для улучшения сгенерированного кода.

Надеюсь, это поможет!

1 голос
/ 08 февраля 2012

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

Ключевым моментом здесь является то, что оно "локально и динамически распределяется" и "время компиляции". Я считаю, что утверждение говорит о том, что эти адреса не могут быть использованы в качестве констант времени компиляции. Это отличается от адреса статически распределенных переменных, которые можно использовать как константы времени компиляции. Один пример этого в шаблонах:

template<int *>
class Klass
{
};

int x;

//OK as it uses address of a static variable;
Klass<&::x> x_klass;


int main()
{
   int y;
   Klass<&y> y_klass; //NOT OK since y is local.
}

Кажется, есть некоторые дополнительные ограничения на шаблоны, которые не позволяют это компилировать:

int main()
{
    static int y;
    Klass<&y> y_klass;
}

Однако другие контексты, использующие константы времени компиляции, могут использовать &y.

И точно так же я ожидаю, что это будет недействительно:

static int * p;

int main()
{
   p = new int();
   Klass<p> p_klass;
}

Поскольку данные p теперь распределяются динамически (даже если p является статическим).

0 голосов
/ 08 февраля 2012

Хороший вопрос.

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

0 голосов
/ 08 февраля 2012
  1. Адрес динамических переменных неизвестен по ожидаемой причине, так как они динамически выделяются из пула памяти.
  2. Адрес локальных переменных неизвестен, поскольку они находятся в области памяти «стека»,Завершение стека программы может быть отложено в зависимости от условий выполнения потока кода.

Например:

void bar(); // forward declare
void foo ()
{
  int i;  // 'i' comes before 'j'
  bar();
}
void bar ()
{
  int j;  // 'j' comes before 'i'
  foo();
}
int main ()
{
  if(...)
    foo();
  else
    bar();
}

Условие if может быть true илиfalse и результат известен только во время выполнения.Исходя из этого, int i или int j будут иметь место при соответствующем смещении в стеке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...