Почему я не могу использовать переменную, вызванную из стека, в отличие от переменной, вызываемой из кучи?
Расположение кучи дает вам больше контроля над памятью.
Кроме того, существуют ограничения, когда дело доходит до переменных стека.
// предупреждение: применяется только к ПК, возможно, не относится к другим архитектурным сооружениям, с которыми я не знаком. (mips, motorola 68000 и т. д. Все, что не связано с x86, в основном).
Переменные, созданные в стеке, могут быть изменены во время выполнения вправо?
Их РАЗМЕР не может измениться.
Стек имеет ограниченный размер. Размер определяется оператором и компилятором. Если стек становится слишком большим, программа умирает из-за переполнения стека. Классический пример:
int main(int argc, char** argv){
char buffer[1024*1024*64];
buffer[0] = 0;
return 0;
}
Эта программа выйдет из строя, если скомпилирована с настройками по умолчанию в Windows, и она также должна аварийно завершить работу с Linux. Это связано с тем, что размер стека по умолчанию составляет 1 МБ на 32-битных окнах и 8 МБ на 32-битном Linux (система может изменить это, хотя, используя ulimit
), и массив размером не более 64 мегабайт не поместится на стек.
Если ваша varaible находится между двумя другими переменными в стеке, вы не можете изменить ее размер, несмотря ни на что. По крайней мере, на x86/64 cpus.
- Вы можете теоретически увеличить размер массива стека, если это последняя вещь в стеке. (если я правильно помню, возможно, была нестандартная функция C, называемая alloca, которая могла бы выделять массивы в стеке). Тем не менее, вы по-прежнему будете использовать ограничение размера стека.
Чтобы понять ПОЧЕМУ, существуют такие ограничения, вам нужно отступить от C++ и узнать немного сборки. Попытайтесь найти книгу, которая охватывает сегменты (данные/код/стек), объясняет, где хранятся обратные адреса функций, и, предпочтительно, сообщает вам об защищенном режиме. Это должно помочь.
Конечно, есть проблема. Ассемблерные знания помогут только конкретному семейству процессоров. Различные процессоры с компилятором C++ могут использовать разные правила.
--update--
Почему позволяют динамической памяти размер массивов, которые будут выделены во время выполнения?
В зависимости от арки размер стека может быть более или менее фиксированным. У вас есть область памяти, зарезервированная для стека, например, по адресам 0x00100000..0x00200000, и все переменные, найденные в стеке, будут где-то в этой области. Расположение новых переменных определяется (если я правильно помню) «указатель стека», который перемещается в направлении, определяемом процессором. Когда вы добавляете новую переменную в стек, указатель стека перемещается (направление движения, определяемое процессором) по переменной размера, а переменная будет располагаться по адресам между старой и новой ячейкой памяти. Поскольку пространство стека может быть ограничено, а также потому, что переменные смежны (плюс адреса возврата функции также хранятся в стеке), вы не можете внезапно замять массив 2 ГБ в середине его. Основная проблема заключается не в ограниченном размере, а в смежных друг с другом переменных.
Теперь HEAP отличается. Распределение кучи, в теории, может вернуть вам любой адрес из всего адресного пространства, но на практике некоторые адреса будут зарезервированы (на 32-битных окнах вы, например, имеете всего 2,3 ГБ, например, из всего пространства 4 ГБ). Поскольку у вас много свободного места, и поскольку выделенные блоки не должны быть смежными, вы можете свободно выделять большой массив и (теоретически) даже изменять их размер (на практике такие функции, как realloc, вероятно, просто создают новый массив, копируют старое содержимое в новый массив, затем уничтожить старый массив).
Обратите внимание, что дополнительные сведения скрыты.Например, адреса, возвращаемые вам функциями распределения кучи, не являются физическими адресами, а виртуальными адресами, и на самом деле ОС может перемещать вашу программу в физической памяти, а (виртуальные) адреса, используемые в программе, остаются неизменными.
Вот почему я предлагаю прочитать сборку. вам не нужно изучать сборку в глубину, но, имея некоторое общее представление о том, что происходит за кулисами, обязательно поможет.
Динамическое распределение позволяет выбрать размер массива во время выполнения. Размер объектов, выделенных в стек, должен быть известен во время компиляции. – juanchopanza
Вы не можете манипулировать размером массива во время выполнения, независимо от того, динамически ли он распределен или нет. –
@juanchopanza - не соответствует действительности (по крайней мере, не соответствует действующему стандарту). Вы можете сделать что-то вроде 'int a [n]' где 'n' - параметр, переданный функции –