(Я не буду беспокоить эти методы в зависимости от конкретных платформ для «лучших» решений. Они создают проблемы , ограничивая языковой дизайн и удобство использования, с небольшим выигрышем. Для ответов «просто работайте» над Linux и Windows, см. Выше.)
Прежде всего, в смысле C, вы не можете сделать это портативным способом . На самом деле, ISO C не предписывает вообще никакого «стека». Педантично даже кажется, что когда автоматическое выделение объектов завершилось неудачно, поведение буквально не определено, согласно пункту 4p2 - просто нет гарантии, что произойдет, если вызовы будут вложены слишком глубоко. Вы должны полагаться на некоторые дополнительные предположения реализации ( ISA или OS ABI ), чтобы сделать это, так что вы получите C + что-то еще, а не только C. Машинный код времени выполнения поколение также не переносимо на уровне C.
(Кстати, ISO C ++ имеет понятие разматывание стека , но только в контексте обработки исключений. И до сих пор нет гарантии переносимого поведения при переполнении стека; хотя это, кажется, не определено, не определено.)
Кроме того, чтобы ограничить глубину вызова, все способы имеют дополнительные затраты времени выполнения. Стоимость будет довольно легко заметной, если не будет каких-либо аппаратных средств для ее амортизации (например, прогулка по таблице страниц). К сожалению, сейчас это не так.
Единственный переносимый способ, который я нахожу, это не полагаться на собственный стек базовой архитектуры машины. В целом это означает, что вы должны распределять кадры записи активации как часть свободного хранилища (в куче), а не как собственный стек, предоставляемый ISA. Это работает не только для интерпретируемых языковых реализаций, но и для скомпилированных, например, SML / NJ. Такой подход программного стека не всегда приводит к худшей производительности , поскольку он позволяет обеспечить абстракцию более высокого уровня на объектном языке, поэтому программы могут иметь больше возможностей для оптимизации, хотя это маловероятно для наивного интерпретатора.
У вас есть несколько вариантов для достижения этой цели. Один из способов - написать виртуальную машину . Вы можете выделить память и построить в ней стек.
Другой способ - написать сложный асинхронный код стиля (например, батуты или CPS-преобразование ) в вашей реализации вместо этого, полагаясь на менее собственные фреймы вызова как возможный. Как правило, трудно понять, но это работает. Дополнительные возможности, включенные таким способом, являются более легкими оптимизация вызовов по хвосту и более легкими первоклассными захват продолжения.