Например, этот код вызовет переполнение стека из-за бесконечной рекурсии, что приведет к сбою программы. Есть ли способ обработать это исключение, избежать сбоя и перейти к следующему выполнению инструкции?
#include <iostream>
#include <exception>
template <typename T>
T sum(T firstValue, T secondValue)
{
try{
return firstValue + secondValue;
}
catch(const std::exception &e)
{
std::cerr << "Caught exception: " << e.what() << '\n';
}
}
void causeStackOverflow() {
causeStackOverflow();
}
int main() {
std::cout << "Sum of 3 & 4 is: " << sum(3, 4) << '\n';
try {
causeStackOverflow();
}
catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << '\n'; // If anything can be done here so program can recover and next line can execute?
}
std::cout << "Sum of 1 & 2 is: " << sum(1, 2) << '\n';
return 0;
}
Должен быть какой-то способ, который можно использовать здесь, чтобы сделать это. Другой вопрос, нужно ли это делать, даже если это возможно?
Есть ли способ, который может с некоторой вероятностью предсказать, что произойдет переполнение стека? Значит, мы можем предпринять какие-то действия, чтобы избежать этого?
Я думал, что это выполнимо, поэтому я попытаюсь реализовать что-то в своем проекте, чтобы избежать сбоя, восстановить и продолжить работу.
Да, собственно, на винде есть. Но я не уверен, есть ли у него плохие побочные эффекты. Вы должны поймать ошибку EXCEPTION_STACK_OVERFLOW
с помощью SEH (__try/__except), а затем вызвать _resetstkoflw.
@StewieGGriffin что-то выполнимое не означает, что вы должны это делать. Переполнение стека оставляет приложение с поврежденным стеком и, следовательно, с неопределенным поведением. Лучшее решение — исправить переполнение стека, а не просто пытаться его обойти.
@StewieGGriffin «У моей машины лопнули две шины. Я хочу продолжать водить машину». -- Это то, о чем вы в основном спрашиваете?
Мое правило: максимальный уровень рекурсии для рекурсивной функции должен быть O(log n). Наличие уровня O(n), подобного этой реализации, напрашивается на неприятности: он быстро выйдет из строя с (не очень) большим n
. И этот тип рекурсивной функции можно очень легко преобразовать в цикл, который легче понять, легче отлаживать, безопаснее и быстрее.
Хорошо работающая функция должна генерировать исключение до возникновения ошибки. Например, проверив его параметры на наличие значений, которые вызовут ошибку.
@PaulMcKenzie - Нет, я больше хочу заранее узнать, что мои шины лопнут, и есть ли какие-то меры, с помощью которых я могу следить за состоянием своих шин и предпринимать какие-либо действия до того, как они лопнут.
@StewieGGriffin Как только выдается исключение, шины взрываются. Любое действие должно быть до исключения. Это то, что предлагали другие комментарии.
Ограничения переполнения стека и их обработка зависят от платформы и компилятора. Поведение не определено стандартом. Стандарты C++ только указывают, что компиляторы должны документировать свои ограничения. См. Что стандарт C++ говорит о переполнении стека?
Ответ ниже работал для вас?
Вы можете обратиться к документу MSDN для переполнения стека отладки.
Используйте команду !analyze, чтобы убедиться, что у нас действительно есть проблема с нашей петлей.
dt _TEB может использоваться для отображения информации.
Сложно поймать исключение, потому что ОС убьет процесс. Для справки: C++: получение исключения из-за переполнения стека?.
В Windows с Microsoft Visual C++ вы можете обрабатывать переполнение стека с помощью структурированной обработки исключений (SEH) следующим образом:
void causeStackOverflow() {
causeStackOverflow();
}
// Filter for the stack overflow exception.
// This function traps the stack overflow exception, but passes
// all other exceptions through.
int stack_overflow_exception_filter(int exception_code)
{
if (exception_code == EXCEPTION_STACK_OVERFLOW)
{
// Do not call _resetstkoflw here, because
// at this point, the stack isn't yet unwound.
// Instead, signal that the handler (the __except block)
// is to be executed.
return EXCEPTION_EXECUTE_HANDLER;
}
else
return EXCEPTION_CONTINUE_SEARCH;
}
int main() {
// Demonstrate handling a stack overflow 3 times.
for (int i = 0; i < 3; ++i) {
__try {
causeStackOverflow();
}
__except (stack_overflow_exception_filter(GetExceptionCode())) {
std::cout << "Handled!\n";
if (!_resetstkoflw()) {
// Failed to reset the stack. Emergency exit.
exit(-1);
}
}
}
}
Важно: Исключение переполнения стека повреждает защитную страницу, которая перехватывает любые последующие исключения переполнения стека. Вы должны поместить эти защитные страницы, вызвав _resetstkoflw
, если вы хотите иметь возможность восстановиться после другого будущего исключения переполнения стека в том же потоке. Настоятельно рекомендую прочитать весь раздел примечаний в документации.
Функция _resetstkoflw восстанавливается после состояния переполнения стека, позволяя программе продолжать работу, а не завершаться фатальной ошибкой исключения. Если функция
_resetstkoflw
не вызывается, после предыдущего исключения нет защитных страниц. В следующий раз, когда произойдет переполнение стека, исключений не будет вообще, и процесс завершится без предупреждения.
Определенно не в стандартном С++, и даже если вы используете другие параметры (SEH), я не думаю, что вы сможете поймать этот, поскольку он считается неисправимой ошибкой программирования. Вы никогда не должны программировать бесконечную рекурсию на C++, по крайней мере, до тех пор, пока хвостовая рекурсия не будет реализована в стандарте.