2016-07-22 8 views
3

Чтение this great tutorial о Stack vs Heap, у меня есть дублет об этой фразе: Вся память, выделенная в стеке, известна во время компиляции.Что это значит: «Вся память, выделенная в стеке, известна во время компиляции»?

Я имею в виду, если я в for цикле, который зависит от пользовательского ввода (i от 0 до X), и в for я выделить память на стеке (например, создавать новые экземпляры некоторых классов и положить внутрь class-container), он не может знать, как будет расти стек при компиляции программы (он пропускает вход от пользователя).

Я что-то не понимаю?

+0

Вы должны добавить код для своего гипотетического сценария. Я думаю, что @DevSolar понял это правильно, но было бы менее двусмысленно, если бы вы предоставили код, о котором вы думаете. –

ответ

4

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

void f(int n) 
{ 
    int x = n * 10; 
    if(x == 0) return; 

    std::cout << x << std::endl; 
    f(n - 1); 
} 

int main() 
{ 
    int n; 
    std::cout << "Enter n: " << std::endl; 
    std::cin >> n; 
    f(n); 
} 

Здесь, очевидно, количество призываний f, рекурсивной функции, зависит от n, введенного пользователем, так что для любого данного экземпляра компилятор не может знать, точный адрес памяти локальной переменной x в f. Однако, что это делает знает x смещение от местного стек кадров, что я считаю примером. Фрейм стека - это локальная область стека, подготовленная каждый раз при вызове функции. В пределах данного кадра стека локальные переменные на самом деле являются известными постоянными смещениями относительно начала фрейма стека. Это «начало» сохраняется в стандартном регистре в каждом вызове, поэтому весь компилятор должен сделать, чтобы найти адрес любого локального, чтобы применить его фиксированное известное смещение к этому динамическому «базовому указателю».

+1

Так что известно во время компиляции - размер каждого 'стекового кадра'. Но не сколько кадров стека будет. Вы это говорите? – markzzz

+1

Именно это. Поиск переменной в определенном стеке стека имеет форму «BP - x», где «BP» - это регистр, представляющий «базовый указатель» для этого фрейма (неизвестный во время компиляции), а 'x' является компиляцией постоянная времени для рассматриваемой переменной (например, «BP - 8»). Знак '-' существует, потому что стек растет вниз, поэтому от базового указателя – Smeeheey

1

В рассматриваемом случае STACK означает память, выделенную компилятором для локальных переменных, определенных в вашем методе. «Вы выделяете» (в кавычках, потому что это делается для вас) эту память, когда определить переменную как:

void myMethod(int x) 
{ 
    int y; 
    for (y = 0; y < 10; y++) 
    { 
     int z = x + y; 
    } 
} 

Все x, y и z в этом примере локальных переменных, выделенных в стеке.

При создании некоторых экземпляров с new оператором, вы выделяете память в куче (и здесь вы выделяете без кавычек), а адрес магазина этой выделенной памяти вы, вероятно, использовать переменный указатель, выделенные в стеке:

int * p = new int[10]; 

В настоящее время p - это локальная переменная (хранится в стеке), которая хранит адрес памяти, выделенной в куче для массива из 10 целых чисел.

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

3

Я имею в виду, если я нахожусь в цикле, который зависит от пользовательского ввода (от 0 до X), и внутри него я выделяю память в стеке (например, создаю новые экземпляры некоторых классов и помещается внутри класса-контейнера), он не может знать, как будет расти стек при компиляции программы (он пропускает вход от пользователя).

Так у вас есть контейнер класса ...

std::vector<SomeClass> vec; 

... это на стеке. Внутри цикла вы создаете новый экземпляр некоторого класса ...

for (size_t i = 0; i < X; ++i) 
{ 
    SomeClass x; 

... который находится на стеке. Когда вы положили это в контейнер ...

vec.push_back(x); 
} 

... этот контейнер сохранит экземпляр в куче.

У вас всегда есть SomeClass на стеке, и этот факт известен во время компиляции.Стек не растет за пределами одного экземпляра.

Там являются способами расти стека во время выполнения, хотя (например, alloca()), так что общее заявление руководства не совсем правильно.

+0

Но на каждой итерации создается 'SomeClass x'. Если у меня X = 20, в памяти создается 20 разных классов. Не так ли? – markzzz

+0

@paizza: Каждый экземпляр уничтожается в конце его цикла (поскольку он выходит за рамки). На второй итерации вы больше не можете обращаться к 'x' с первой итерации. То, что вы храните в контейнере, является * копией * 'x', и оно находится в куче. – DevSolar

+0

@paizza: Также обратите внимание, что я говорю только о стеке * кадра * функции * current *. Проверьте ответ Smeeheey на довольно корректные утверждения об использовании * полной памяти стека. – DevSolar

 Смежные вопросы

  • Нет связанных вопросов^_^