Почему компилятор не знает адреса локальных переменных во время компиляции?
Что означает следующее утверждение?
локальные и динамически распределенные переменные имеют адреса, которые не известны компилятору при компиляции исходного файла
Я раньше думал, что локальные переменные выделяют адреса во время компиляции, но этот адрес может измениться, когда он выйдет из области, а затем снова войдет в область во время вызова функции. Но выше заявлении addresess локальных переменных нет известен компилятором. тогда как распределяются локальные переменные? Почему адреса глобальных переменных могут быть известны во время компиляции??
кроме того, не могли бы вы предоставить хорошую ссылку, чтобы прочитать, как распределяются локальные переменные и другие?
спасибо заранее!
4 ответов
приведенная выше цитата верна-компилятор обычно не знает адреса локальных переменных во время компиляции. Тем не менее, компилятор, вероятно, знает смещение от базы кадра стека, в котором будет расположена локальная переменная, но в зависимости от глубины стека вызовов, это может перевести в другой адрес во время выполнения. В качестве примера рассмотрим этот рекурсивный код (который, кстати, ни в коем случае не является хорошим кодом!):
int Factorial(int num) {
int result;
if (num == 0)
result = 1;
else
result = num * Factorial(num - 1);
return result;
}
в зависимости от параметр num
этот код может в конечном итоге сделать несколько рекурсивных вызовов, так что будет несколько экземпляров result
в памяти, каждый из которых имеет разное значение. Следовательно, компилятор не может знать, куда все они пойдут. Однако, каждый экземпляр result
вероятно, будет смещено одинаковое количество от основания кадра стека, содержащего каждый Factorial
invocation, хотя теоретически компилятор может делать другие вещи, такие как оптимизация этого кода, чтобы была только одна копия result
.
как правило, компиляторы выделяют локальные переменные, поддерживая модель кадра стека и отслеживая, где находится следующее свободное местоположение в кадре стека. Таким образом, локальные переменные могут быть выделены относительно начала кадра стека, и когда функция вызывается, этот относительный адрес может использоваться вместе с адресом стека для поиска местоположения этой переменной в конкретном кадре стека.
глобальные переменные, с другой стороны, их адреса могут быть известны во время компиляции. Они отличаются от локальных в первую очередь тем, что в программе всегда есть одна копия глобальной переменной. Локальные переменные могут существовать 0 или более раз в зависимости от того, как исполнение идет. В результате того, что существует одна уникальная копия global, компилятор может жестко закодировать для нее адрес.
что касается дальнейшего чтения, если вы хотите довольно углубленную обработку того, как компилятор может выкладывать переменные, вы можете выбрать копия компиляторы: принципы, методы и инструменты, второе издание по АХО, Лам, сети и Ульман. Хотя большая часть этой книги касается других методов построения компиляторов, большая часть книги посвящена реализации генерации кода и оптимизации, которые могут быть использованы для улучшения сгенерированного кода.
надеюсь, что это помогает!
на мой взгляд, утверждение не говорит о доступе среды выполнения к переменным или области, но пытается сказать что-то более тонкое.
ключ здесь в том, что его "локальное и динамически распределенное" и "время компиляции". Я считаю, что утверждение говорит о том, что эти адреса не могут использоваться в качестве констант времени компиляции. Это контрастирует с адресом статически распределенных переменных, которые могут использоваться в качестве констант времени компиляции. Одним из примеров этого является шаблоны:
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 является статическим).
- адрес динамических переменных не известен по ожидаемой причине, как они выделяются динамически из пула памяти.
- адрес локальных переменных неизвестен, так как они находятся на "стек" области памяти. Stack winding-размотка программы может отложить на основе условий выполнения потока кода.
например:
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
будет иметь место при соответствующем смещении на стеке.
Это хороший вопрос.
при выполнении кода программа загружается в память. Затем локальная переменная получает адрес. Во время компиляции исходный код преобразуется в код машинного языка, чтобы его можно было выполнить