Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Нужно ли очищать стек при выкидывании Exception ? в рекурсивных функциях, например 
:(
    Опции темы
S.A.P.
Дата 23.10.2005, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2664
Регистрация: 11.6.2004

Репутация: 9
Всего: 71



Раньше никогда не обращал внимания, теперь решил разобраться с вопросом.

Допустим имеется код:

Код


struct Overload {
    const char *p;
    Overload( const char *q ) { p = q; }
};

//. . .
 
void f1( int depth )
{
    if ( depth >= 10 ) throw Overload( "f1" );
    f2( depth + 1 );
}

void f2( int depth )
{
    if ( depth >= 10 ) throw Overload( "f2" );
    f3( depth + 1 );
}

void f3( int depth )
{
    if ( depth >= 10 ) throw Overload( "f3" );
    f1( depth + 1 );
}

// . . .

    try {
        f1( 0 );
    }
    catch ( Overload e ) {
        cout << "exception executed in function " << e.p << endl;
    }


Алгоритм действий я как понимаю такой: функции вызывают друг друга, передавая параметр на 1 больше своего, то дех пор, пока одна из функций не получит 10. В таком случае генерируется исключение и управление передается в блок catch.

Если расматривать все на более низком уровне, то очевидно параметры подаются через стек и стек должен восстанавливаться при завершении функции. А что тогда будет в этом случае, ведь ни одна функция фактически не завершиться? Будет ли очищаться стек, либо его надо очищать вручную, либо вобще избегать таких конструкий? Что скажите?

Это сообщение отредактировал(а) Perchilla - 23.10.2005, 21:31
PM MAIL   Вверх
srd
Дата 23.10.2005, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 27
Всего: 49



Стек очищается, деструкторы стековых объектов вызываются. Короче, ты ищешь грабли там, где их нет.


--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
S.A.P.
Дата 23.10.2005, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2664
Регистрация: 11.6.2004

Репутация: 9
Всего: 71



srd smile .

спасибо,буду знать на будущее smile
PM MAIL   Вверх
Void
Дата 23.10.2005, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Perchilla
В этом, собственно, и суть исключений (и заодно идиомы RAII) smile


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
S.A.P.
Дата 23.10.2005, 22:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2664
Регистрация: 11.6.2004

Репутация: 9
Всего: 71



Кстати, VC 7 исключения обрабатывает очень медленно. Попробуйте вот такую штуку прогнать, не дождетесь smile .
Код

struct Bench_exc {
    int i;
    Bench_exc( int j ) { i = j; };
};

int bench( int i )
{
    if ( i == 20 ) return i+100;
    throw Bench_exc( i+100 );
}

// . . .

   int j;

    for (int i = 0; i < 1000000; ++i) {
        try {
            bench( 10 );
        }
        catch ( Bench_exc e ) {
            j += e.i;
        }
    }


на MinGW - меньше секунды.

Это сообщение отредактировал(а) Perchilla - 23.10.2005, 22:11
PM MAIL   Вверх
Void
Дата 23.10.2005, 22:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Perchilla
VC++ реализует исключения с помощью SEH, а MinGW каким-то своим способом. Если взглянуть на результаты GetProcessTimes, то бинарик VC++ бОльшую часть времени проводит в kernel time.
Подробности реализации исключений в VC++ расписаны здесь.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Helicopterr
Дата 23.10.2005, 22:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 355
Регистрация: 20.8.2005
Где: Stavropol

Репутация: 2
Всего: 3



Пользуясь темой, хочу спросить...
Какова максимальная глубина рекурсии в с++?
И несёт ли использование рекурсии какие-либо опасности типа стек переполнен и др.?



--------------------
people can fly
PM MAIL   Вверх
Void
Дата 23.10.2005, 22:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Цитата(Helicopterr @ 24.10.2005, 00:32)
Какова максимальная глубина рекурсии в с++?

Зависит от выделенного размера стека smile
Цитата(Helicopterr @ 24.10.2005, 00:32)
И несёт ли использование рекурсии какие-либо опасности типа стек переполнен и др.?

Именно в переполнении стека и заключаются единственные возможные грабли. Поэтому порядок глубины рекурсии для заданного алгоритма крайне желательно знать заранее.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Helicopterr
Дата 23.10.2005, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 355
Регистрация: 20.8.2005
Где: Stavropol

Репутация: 2
Всего: 3



Цитата(Void @ 23.10.2005, 22:35)

Зависит от выделенного размера стека smile

а если более конкретно вот например рекурсивный код нахождения факториала-
Код

#include "stdafx.h"

long double recfactorial(short chislo);


int _tmain()
{  
    
    short chislo;
    cin>>chislo;
    cout<<"\nFactorial "<<chislo<<" = "<<recfactorial(chislo);
    return 0;
}
long double recfactorial(short chislo)
{
if(chislo<=1)
return 1;
else
return(chislo*recfactorial(chislo-1));
}




--------------------
people can fly
PM MAIL   Вверх
Void
Дата 24.10.2005, 09:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Helicopterr
Глубина рекурсии O(N), каждый вызов recfactorial занимает в стеке как минимум sizeof(short) + адрес возврата. Линкер VC++ по умолчанию задает размер стека 1 Мб. Получаем максимально возможную глубину рекурсии порядка 10^5, что все равно выходит за диапазон short (пар-р chislo).


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
En_t_end
Дата 24.10.2005, 10:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2074
Регистрация: 4.12.2004

Репутация: 1
Всего: 20



Вопрос в тему... присутсвует кстати в большиснстве тестов по C++.
Для каких обьектов будут вызваны деструкторы при возникновении исключения и обработки его своим catch'ом. Для всех ? Или только для обьктов созданных внутри блока try ?
PM MAIL ICQ Skype GTalk Jabber   Вверх
pablo
Дата 24.10.2005, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 320
Регистрация: 12.2.2005
Где: Вильнюс, Литва

Репутация: 4
Всего: 6



Есть такое явление, называемое "Размотка Стека". Это произходит тогда, когда исключение вознокло в дискрукторе объекта, и небыло перехвачено. Если ф-ция создающая объект глубоко вложена, то размотка приобретает колласальные размеры, и может вызвать трудновыловимые ошибки. Одно из средств предотвращения размотки стека - использование auto_ptr.




--------------------
Первый блин всегда похож на сферу, иногда бывает и куб.
PM MAIL ICQ   Вверх
srd
Дата 24.10.2005, 11:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 27
Всего: 49



En_t_end
А пример кода можно? А то я не понял, что именно ты спрашиваешь.


--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
Chaos A.D.
Дата 24.10.2005, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 172
Регистрация: 16.1.2005
Где: 09 RUS

Репутация: 6
Всего: 7



2 pablo

К вопросу о "размотке" стека - если ты о том, что по заморски называется Stack Unwinding, то тут ты ошибаешься. Раскруткой стека (мне это название больше нравится) называется процесс удаления стековых объектов и возврат из вложенных функций до уровня первого попавшегося catch-блока, если он есть конечно. От твоего определения мое отличается тем, что раскрутка стека происходит в любом случае. Если же исключение сгенерировано из деструктора, то это особый случай. Тут, если деструктор, сгегеривший исключение, был вызыван в процессе раскрутки стека, вызывается abort();

Цитата

Одно из средств предотвращения размотки стека - использование auto_ptr


Размотка стека - это Хорошая Вещь, придуманная как часть механизма исключений, и предотвращать её, имхо, довольно глупо.

Это сообщение отредактировал(а) Chaos A.D. - 24.10.2005, 12:11
--------------------
Надо смеяться над тем, что тебя мучит, иначе не сохранишь равновесия, иначе мир сведет тебя с ума...Ken Kesey - One Flew Over The Cocoo's Nest
PM MAIL   Вверх
pablo
Дата 24.10.2005, 12:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 320
Регистрация: 12.2.2005
Где: Вильнюс, Литва

Репутация: 4
Всего: 6



2Chaos A.D. Может быть, спасибо что исправил.



--------------------
Первый блин всегда похож на сферу, иногда бывает и куб.
PM MAIL ICQ   Вверх
En_t_end
Дата 24.10.2005, 16:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2074
Регистрация: 4.12.2004

Репутация: 1
Всего: 20



srd
Some obj;
try
{
Some obj2;
}
catch(...)
{
//
//
//
}
Для каких обьектов будет вызван деструктор при возникновении исключения ?
PM MAIL ICQ Skype GTalk Jabber   Вверх
srd
Дата 24.10.2005, 16:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 27
Всего: 49



Так как в твоём примере нигде нет инструкции throw, то могу предположить, что исключение бросается внутри конструктора класса Some.
а) Если исключение будет брошено при конструировании объекта obj, то будут вызваны деструкторы для тех объектов-членов, для которых успели отработать конструкторы. Для остальных объектов-членов деструкторы вызваны не будут. Деструктор для самого obj и ob2 тоже вызван не будет.
б) Если исключение будет брошено при конструировании obj2, то ни для самого obj, ни для его членов деструкторы не вызовутся. Для obj2 деструктор тоже не будет вызван. Будут вызваны деструкторы только для объектов-членов obj2, для которых успели отработать конструкторы до исключения.



--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
En_t_end
Дата 24.10.2005, 17:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 2074
Регистрация: 4.12.2004

Репутация: 1
Всего: 20



srd
Все верно...
PM MAIL ICQ Skype GTalk Jabber   Вверх
Helicopterr
  Дата 24.10.2005, 22:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 355
Регистрация: 20.8.2005
Где: Stavropol

Репутация: 2
Всего: 3



Цитата(Void @ 24.10.2005, 09:22)
Линкер VC++ по умолчанию задает размер стека 1 Мб. Получаем максимально возможную глубину рекурсии порядка 10^5, что все равно выходит за диапазон short (пар-р chislo).

возвращает то ф-ция long double а не short.
вобщем при использовании рекурсии надо считать сколько в этой функции выделяется памяти под переменные и умножать на предполагаемую глубину рекурсии а затем сравнвать это с 1Мб? И если получилось больше то рекурсию не использовать?

И ещё правда, что рекурсия работает более медленно, чем тот же алгоритм, реализованный на циклах?

Это сообщение отредактировал(а) Helicopterr - 24.10.2005, 22:42


--------------------
people can fly
PM MAIL   Вверх
Void
Дата 24.10.2005, 22:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Цитата(Helicopterr @ 25.10.2005, 00:41)
возвращает то ф-ция long double а не short.

При чем тут возвращаемое значение? Глубина рекурсии в твоем примере равна chislo - 1.
Цитата(Helicopterr @ 25.10.2005, 00:41)
вобщем при использовании рекурсии надо считать сколько в этой функции выделяется памяти под переменные

+ параметры ф-ции. Но точный размер можно узнать, только посмотрев ассемблерный листинг сгенерированного кода.
Цитата(Helicopterr @ 25.10.2005, 00:41)
И ещё правда, что рекурсия работает более медленно, чем тот же алгоритм, реализованный на циклах?

Вообще говоря, да, но закладываться на это не стоит. Нормально раскрутить в цикл можно только хвостовую рекурсию, для остальных случаев все равно приходиться эмулировать стек вручную, что не особо повышает скорость, но существенно снижает читаемость кода.
Цитата(Helicopterr @ 25.10.2005, 00:41)
И если получилось больше то рекурсию не использовать?

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


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Helicopterr
  Дата 24.10.2005, 23:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 355
Регистрация: 20.8.2005
Где: Stavropol

Репутация: 2
Всего: 3



Цитата(Void @ 24.10.2005, 22:54)
А производить такие расчеты для каждой рекурсивной ф-ции - имхо, странное занятие.

Cпасибо согласен.
Однако вроде бы программный стек увеличивается динамически по мере объявления в программе новых переменных, отхватывая динамическую память "куча"?
Что за информация об огораничении стека линкером в 1Мб?

Это сообщение отредактировал(а) Helicopterr - 24.10.2005, 23:09


--------------------
people can fly
PM MAIL   Вверх
Void
Дата 24.10.2005, 23:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Цитата(Helicopterr @ 25.10.2005, 01:08)
Однако вроде бы программный стек увеличивается динамически по мере объявления в программе новых переменных, отхватывая динамическую память "куча"?

Да, это так, но 1 Мб здесь указывает именно на предельный размер, который система позволит выделить. Когда стек превышает указанный при создании потока предел, выбрасывается исключение.

P.S. Забавный компилятор VC++ 8.0:
Код
void rectest(int param) {
    printf("%i\n", param);
    rectest(param + 1);
}

Цитата
warning C4717: 'rectest' : recursive on all control paths
, function will cause runtime stack overflow

Код
_rectest PROC
    push    esi
    mov    esi, DWORD PTR _param$[esp]
$LL3@rectest:
    push    esi
    push    OFFSET $SG2462
    call    _printf
    add    esp, 8
    add    esi, 1
    jmp    SHORT $LL3@rectest
_rectest ENDP

Без комментариев smile Но за оптимизацию хвостовой рекурсии +1.
P.P.S. Впрочем, и за предупреждение тоже - все-таки, нехилый там анализ кода производится.

Это сообщение отредактировал(а) Void - 24.10.2005, 23:37


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Helicopterr
Дата 24.10.2005, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 355
Регистрация: 20.8.2005
Где: Stavropol

Репутация: 2
Всего: 3



Void
Как вцелом компилятор VC++ 8.0? Лучше 7-го?


--------------------
people can fly
PM MAIL   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1169 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.