_resetstkoflw
Obnoví se z přetečení zásobníku.
Důležité
Toto rozhraní API nelze použít v aplikacích, které se spouští v prostředí Windows Runtime. Další informace najdete v tématu Funkce CRT, které nejsou v aplikacích Univerzální platforma Windows podporované.
Syntaxe
int _resetstkoflw( void );
Vrácená hodnota
Nenulové, pokud je funkce úspěšná, nula, pokud selže.
Poznámky
Funkce _resetstkoflw
se obnoví z podmínky přetečení zásobníku, takže program může pokračovat místo selhání se závažnou chybou výjimky. _resetstkoflw
Pokud funkce není volána, po předchozí výjimce nejsou žádné strážné stránky. Při příštím přetečení zásobníku nejsou žádné výjimky a proces se ukončí bez upozornění.
Pokud vlákno v aplikaci způsobí EXCEPTION_STACK_OVERFLOW
výjimku, vlákno opustilo jeho zásobník v poškozeném stavu. Tato výjimka se liší od jiných výjimek, jako EXCEPTION_ACCESS_VIOLATION
jsou nebo EXCEPTION_INT_DIVIDE_BY_ZERO
, kde zásobník není poškozen. Zásobník je nastaven na libovolně malou hodnotu při prvním načtení programu. Zásobník pak roste na vyžádání, aby vyhovoval potřebám vlákna. Růst na vyžádání se implementuje umístěním stránky s PAGE_GUARD
přístupem na konec aktuálního zásobníku. Další informace naleznete v tématu Vytváření strážových stránek.
Když kód způsobí, že ukazatel zásobníku bude odkazovat na adresu na této stránce, dojde k výjimce a systém provede následující tři věci:
Odebere
PAGE_GUARD
ochranu na stránce stráže, aby vlákno mohl číst a zapisovat data do paměti.Přidělí novou strážnou stránku, která se nachází na jedné stránce pod poslední stránkou.
Znovu spustí instrukce, která vyvolala výjimku.
Tímto způsobem může systém automaticky zvětšit velikost zásobníku pro vlákno. Každé vlákno v procesu má maximální velikost zásobníku. Velikost zásobníku je nastavena v době kompilace pomocí /STACK
možnosti (Stack Allocations) nebo STACKSIZE
příkazem v .def
souboru projektu.
Při překročení této maximální velikosti zásobníku systém provede následující tři věci:
Odebere ochranu PAGE_GUARD na stránce ochrany, jak jsme popsali dříve.
Pokusí se přidělit novou stránku ochrany pod poslední stránkou. Přidělení ale selže, protože byla překročena maximální velikost zásobníku.
Vyvolá výjimku, aby ji vlákno mohlo zpracovat v bloku výjimky.
V tomto okamžiku už zásobník nemá strážnou stránku. Když program příště zvětší zásobník na místo, kde zapíše mimo konec zásobníku, způsobí narušení přístupu.
Volání _resetstkoflw
pro obnovení ochranné stránky při každém dokončení obnovení po výjimce přetečení zásobníku Tuto funkci lze volat z hlavního __except
těla bloku nebo mimo __except
blok. Existují však určitá omezení, kdy se má použít. _resetstkoflw
nemělo by se volat z:
Výraz filtru.
Funkce filtru.
Funkce volaná z funkce filtru.
Blok
catch
.Blok
__finally
.
V těchto bodech zásobník ještě není dostatečně unwound.
Výjimky přetečení zásobníku se generují jako strukturované výjimky, ne výjimky C++, takže _resetstkoflw
není užitečné v běžném catch
bloku, protože nezachytí výjimku přetečení zásobníku. Pokud _set_se_translator
se však používá k implementaci strukturovaného překladače výjimek, který vyvolává výjimky jazyka C++ (jako v druhém příkladu), výsledkem výjimky přetečení zásobníku je výjimka jazyka C++, kterou lze zpracovat blokem zachycení jazyka C++.
Není bezpečné volat _resetstkoflw
v bloku catch jazyka C++, který je dosažen z výjimky vyvolané funkcí překladače strukturovaných výjimek. V tomto případě se prostor zásobníku neuvolní a ukazatel zásobníku se nenuluje, dokud není blok catchu, i když byly volány destruktory pro všechny destrukovatelné objekty před blokem catch. Tato funkce by se neměla volat, dokud se neuvolní místo zásobníku a ukazatel zásobníku se resetuje. Proto by se mělo volat až po ukončení bloku catch. V bloku catch by se měl použít co nejmenší prostor zásobníku. Přetečení zásobníku, ke kterému dochází v bloku catch, který se pokouší obnovit z předchozího přetečení zásobníku, není možné obnovit. Může způsobit, že program přestane reagovat, protože přetečení v bloku catch aktivuje výjimku, kterou sám zpracovává stejný blok catch.
Existují situace, kdy _resetstkoflw
může selhat i v případě, že se používá ve správném umístění, například v __except
rámci bloku. Na poslední stránce zásobníku nemusí být dostatek místa pro spuštění _resetstkoflw
, aniž byste museli zapisovat na poslední stránku zásobníku, a to ani po odvíjení zásobníku. _resetstkoflw
Potom se nepodaří obnovit poslední stránku zásobníku jako strážnou stránku a vrátí hodnotu 0, což značí selhání. Bezpečné použití této funkce by mělo zahrnovat kontrolu návratové hodnoty, a ne za předpokladu, že je zásobník bezpečný pro použití.
Strukturované zpracování výjimek nezachytí STATUS_STACK_OVERFLOW
výjimku při kompilaci /clr
aplikace (viz /clr
(common language runtime compilation)).
Ve výchozím nastavení je globální stav této funkce vymezen na aplikaci. Chcete-li toto chování změnit, přečtěte si téma Globální stav v CRT.
Požadavky
Rutina | Požadovaný hlavičkový soubor |
---|---|
_resetstkoflw |
<malloc.h> |
Další informace o kompatibilitě najdete v tématu Kompatibilita.
Knihovny: Všechny verze funkcí knihovny CRT.
Příklad
Následující příklad ukazuje doporučené použití _resetstkoflw
funkce.
// crt_resetstkoflw.c
// Launch program with and without arguments to observe
// the difference made by calling _resetstkoflw.
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
void recursive(int recurse)
{
_alloca(2000);
if (recurse)
recursive(recurse);
}
// 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(int ac)
{
int i = 0;
int recurse = 1, result = 0;
for (i = 0 ; i < 10 ; i++)
{
printf("loop #%d\n", i + 1);
__try
{
recursive(recurse);
}
__except(stack_overflow_exception_filter(GetExceptionCode()))
{
// Here, it is safe to reset the stack.
if (ac >= 2)
{
puts("resetting stack overflow");
result = _resetstkoflw();
}
}
// Terminate if _resetstkoflw failed (returned 0)
if (!result)
return 3;
}
return 0;
}
Ukázkový výstup bez argumentů programu:
loop #1
Program přestane reagovat bez provádění dalších iterací.
S argumenty programu:
loop #1
resetting stack overflow
loop #2
resetting stack overflow
loop #3
resetting stack overflow
loop #4
resetting stack overflow
loop #5
resetting stack overflow
loop #6
resetting stack overflow
loop #7
resetting stack overflow
loop #8
resetting stack overflow
loop #9
resetting stack overflow
loop #10
resetting stack overflow
Popis
Následující příklad ukazuje doporučené použití _resetstkoflw
v programu, kde strukturované výjimky jsou převedeny na výjimky jazyka C++.
Kód
// crt_resetstkoflw2.cpp
// compile with: /EHa
// _set_se_translator requires the use of /EHa
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
#include <eh.h>
class Exception { };
class StackOverflowException : Exception { };
// Because the overflow is deliberate, disable the warning that
// this function will cause a stack overflow.
#pragma warning (disable: 4717)
void CauseStackOverflow (int i)
{
// Overflow the stack by allocating a large stack-based array
// in a recursive function.
int a[10000];
printf("%d ", i);
CauseStackOverflow (i + 1);
}
void __cdecl SEHTranslator (unsigned int code, _EXCEPTION_POINTERS*)
{
// For stack overflow exceptions, throw our own C++
// exception object.
// For all other exceptions, throw a generic exception object.
// Use minimal stack space in this function.
// Do not call _resetstkoflw in this function.
if (code == EXCEPTION_STACK_OVERFLOW)
throw StackOverflowException ( );
else
throw Exception( );
}
int main ( )
{
bool stack_reset = false;
bool result = false;
// Set up a function to handle all structured exceptions,
// including stack overflow exceptions.
_set_se_translator (SEHTranslator);
try
{
CauseStackOverflow (0);
}
catch (StackOverflowException except)
{
// Use minimal stack space here.
// Do not call _resetstkoflw here.
printf("\nStack overflow!\n");
stack_reset = true;
}
catch (Exception except)
{
// Do not call _resetstkoflw here.
printf("\nUnknown Exception!\n");
}
if (stack_reset)
{
result = _resetstkoflw();
// If stack reset failed, terminate the application.
if (result == 0)
exit(1);
}
void* pv = _alloca(100000);
printf("Recovered from stack overflow and allocated 100,000 bytes"
" using _alloca.");
return 0;
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Stack overflow!
Recovered from stack overflow and allocated 100,000 bytes using _alloca.