Pull to refresh

Comments 23

Про стек я бы ещё добавил, что в случае с примитивными типами данных никакой очистки не требуется — по выходу из функции указатель стека меняется, значения остаются где были, и затем естественным образом перезаписываются следующим вызовом. Или просто остаются там лежать в роли т.н. "мусора со стека".

Стек используется для статичного выделения памяти.

Если "статичный" в том смысле, что один раз выделил и потом только пользуешься, то нет — если функция вызывается 100 раз, то 100 раз на стеке будет отводиться место под её локальные данные.


Если "статичный" в том смысле, что размер выделяемого места известен на этапе компиляции, то тоже необязательно. C/C++/C#, например, позволяют в рантайме выделять на стеке области произвольного размера, лишь бы хватало свободного места.


Он организован по принципу «последним пришёл — первым вышел» (LIFO). Можно представить стек как стопку книг — разрешено взаимодействовать только с самой верхней книгой: прочитать её или положить на неё новую.

В стеке вызовов разрешено взаимодействовать с любой книгой в стопке. Например, функция может передать адрес/ссылку на свои локальные данные на стеке другим вызываемым ею функциям, а те передать ещё дальше. В результате можно взаимодействовать с данными не только на вершине стека, но и где-то в глубине.


благодаря упомянутому принципу, стек позволяет очень быстро выполнять операции с данными — все манипуляции производятся с «верхней книгой в стопке». Книга добавляется в самый верх, если нужно сохранить данные, либо берётся сверху, если данные требуется прочитать;

Сам по себе принцип LIFO на скорость не влияет. Если имелось в виду, что благодаря этому принципу у данных на вершине стека больше шансов находиться в кешах процессора и поэтому доступ к ним обычно быстрее, то ладно. Правда, если у какого-то микропроцессора кешей нет, то всё равно что там: LIFO, FIFO или что-то ещё. Хотя это либо древность, либо экзотика.


управление стековой памятью простое и прямолинейное; оно выполняется операционной системой;

Единственное, что делает операционная система со стеком — это выделяет под стек область памяти некоторого размера при создании потока. Ну ещё обрабатывает ситуацию переполнения стека. Оперативное управление находится в руках рантайма или компилятора языка, на котором пишем.

благодаря упомянутому принципу, стек позволяет очень быстро выполнять операции с данными — все манипуляции производятся с «верхней книгой в стопке». Книга добавляется в

Как обычно, слышал звон…
Думаю, подразумевается скорость выделения и освобождения.

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

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

А как средствами C/C++ выделить на стеке область произвольного размера?
alloca()
Большое обсуждение, почему это не лучшая идея.
В частности, там указываются такие проблемы:

-нет в стандарте
-при переполнении будет UB
-отключает некоторые оптимизации (типа вот этих:)
Если «статичный» в том смысле, что размер выделяемого места известен на этапе компиляции, то тоже необязательно. C/C++/C#, например, позволяют в рантайме выделять на стеке области произвольного размера, лишь бы хватало свободного места. alex_zzzz

-если компилятор заинлайнит функцию, которая вызывает внутри себя alloca в цикле, ваш взорвется
И другие проблемы. За подробностями — смотрите обсуждение на SO
Хмм… Можно ли называть «средствами C/C++» некую фичу, которой нет в стандарте и которая поддерживается одним-двумя компиляторами?
Кстати, умеет ли alloca майкрософтовский компилятор?
Умеет.

Я бы сказал, что это дополнительная возможность, но уж никак не часть языка. Строго говоря, сам язык — это то, что входит в стандарт.

VLA (variable length array) входит в C99 как обязательная фича, в C11 как опциональная. Gcc, icc, clang её понимают, msvc игнорирует.


void Foo(int size)
{
    char array[size];
    // ...
}

В C# фича была с рождения:


unsafe void Foo(int size)
{
    byte* p = stackalloc byte[size];
    // ...
}

С версии 7.2 unsafe не требуется:


void Foo(int size)
{
    Span<byte> span = stackalloc byte[size];
    // ...
}

Только стандарт С не оговаривает, где выделяются VLA; некоторые компиляторы просто подставляют вызовы malloc и free в соответствующие места.

Compiler Explorer ― https://godbolt.org/


Потыкал в разные компиляторы. Все, кого попробовал, при использовании VLA мутили что-то с указателем вершины стека.


В принципе, одного примера C# достаточно, чтобы закрыть этот вопрос.

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

типичные структуры данных, которые хранятся в куче — это глобальные переменные

А я думал, что глобальные переменные выделяются в статической области… Или это только в С/С++ так?
Вообще немного странно называть глобальные переменные "структурами данных".

Скорей всего автор статическую область также относит к куче.

Странно. Я подозреваю, что автор привык к ссылочным типам, для которых сам объект всегда в куче создается — даже если ссылка глобальная.

Ядро ОС же все равно выделяет место в памяти для .text и .data в куче перед тем как записать туда эти сегменты исполняемого файла. Значит и глобальные переменные и код лежат в куче с точки зрения ядра, хотя сам процесс об этом ничего не знает.

В каком-то смысле — да, наверное. Но это уже вопрос не языка программирования, а ОСи, которой может и не быть в общем случае.

Ну с точки зрения ядра, да используется page allocator. А вот с точки зрения программы на С — у нее есть статическая память и есть куча (если кто-то предоставляет аллокатор). Прошивки на совсем мелкие МК пишутся без аллокатора, все хранится в статической памяти/на стеке.
Простите, но почему вы используете термин «байт-код» вместо просто «код»? Байт-код используется далеко не во всех перечисленных языках, упомянутых в статье.
Only those users with full accounts are able to leave comments. Log in, please.