_resetstkoflw
从堆栈溢出恢复。
重要
此 API 不能用于在 Windows 运行时中执行的应用程序。 有关详细信息,请参阅通用 Windows 平台应用中不支持的 CRT 函数。
语法
int _resetstkoflw( void );
返回值
如果函数成功,则为非零值;如果失败,则为 0。
备注
_resetstkoflw
函数将从堆栈溢出条件中恢复,让程序继续执行,而不会出现致命异常错误。 如果未调用 _resetstkoflw
函数,则在前一个异常后不会显示保护页。 下次发生堆栈溢出时,将不会有任何异常,且进程会在无警告的情况下终止。
如果应用程序中的线程导致 EXCEPTION_STACK_OVERFLOW
异常,则该线程的堆栈将处于损坏状态。 此异常不同于其他异常,例如EXCEPTION_ACCESS_VIOLATION
或EXCEPTION_INT_DIVIDE_BY_ZERO
,其中堆栈不会损坏。 首次加载程序时,堆栈将设置为任意的较小的值。 然后,堆栈会根据需要增长以满足线程的需求。 按需增长是通过将具有PAGE_GUARD
访问权限的页面置于当前堆栈末尾来实现。 有关更多信息,请参见创建保护页。
当代码导致堆栈指针指向此页上的一个地址时,会发生异常,并且系统将执行以下三个操作:
移除保护页上的
PAGE_GUARD
保护,以便线程可在内存中读取和写入数据。分配一个新的保护页,新的页位于最后一个保护页的下方。
重新运行引发异常的命令。
通过这种方式,系统可以自动增大线程的堆栈大小。 进程中的每个线程都具有最大堆栈大小。 堆栈大小在编译时由/STACK
/STACK(堆栈分配)选项设置,或由项目的.def
文件中的STACKSIZE
语句设置。
当超过最大堆栈大小时,系统将执行以下三个操作:
在保护页上移除 PAGE_GUARD 保护,如前所述。
尝试在最后一个保护页的下方分配新的保护页。 不过,因为超过了最大堆栈大小,分配失败。
引发异常,以便线程可以在异常块中处理它。
此时堆栈不再具有保护页。 下次程序将堆栈增长到超出堆栈末尾的写入位置时,会导致访问冲突。
只要在出现堆栈溢出异常后执行恢复,就会调用 _resetstkoflw
来还原保护页。 可从 __except
块的主体内部或 __except
块的外部调用该函数。 但是,对于何时使用该函数有一些限制。 不应从以下位置调用 _resetstkoflw
:
筛选器表达式。
筛选器函数。
从一个筛选器函数调用的函数。
catch
块。__finally
块。
在这些点上,堆栈尚未充分展开。
堆栈溢出异常是作为结构化异常而非 C++ 异常生成的,因此,_resetstkoflw
在普通 catch
块中没有作用,因为它不会捕获堆栈溢出异常。 但是,如果 _set_se_translator
用于实现引发 C++ 异常的结构化异常转换器(如第二个示例所示),则堆栈溢出异常会导致可由 C++ catch 块处理的 C++ 异常。
在 C++ catch 块中调用 _resetstkoflw
是不安全的,因为这是从通过结构化的异常转换器函数引发的异常到达的。 在这种情况下,不会释放堆栈空间,并且堆栈指针只有在 catch 块之外才会重置,即使已先于 catch 块对任何易损坏的对象调用析构函数。 在释放堆栈空间并且已重置堆栈指针之前,不应调用此函数。 因此,仅在退出 catch 块之后才调用它。 在 catch 块中应尽可能少地使用堆栈空间。 如果 catch 块本身尝试从之前的堆栈溢出中恢复,则在其中发生的堆栈溢出不可恢复。 它可能导致程序停止响应,因为 catch 块中的溢出会触发本身由同一 catch 块处理的异常。
在某些情况下,即使在正确的位置(例如,在 __except
块中)使用 _resetstkoflw
,它也会失败。 如果没有写入堆栈的最后一页,即使展开堆栈后,也可能没有足够的堆栈空间可以执行_resetstkoflw
。 然后,_resetstkoflw
无法将堆栈的最后一页重置为保护页,并返回 0,指示失败。 此函数的安全用法应包括检查返回值而不是假定可安全使用堆栈。
在使用 /clr
编译应用程序时,结构化异常处理将不会捕获 STATUS_STACK_OVERFLOW
异常(请参阅 /clr
(公共语言运行时编译))。
默认情况下,此函数的全局状态范围限定为应用程序。 若要更改此行为,请参阅 CRT 中的全局状态。
要求
例程 | 必需的标头 |
---|---|
_resetstkoflw |
<malloc.h> |
有关兼容性的详细信息,请参阅 兼容性。
库:CRT 库功能的所有版本。
示例
以下示例说明了 _resetstkoflw
函数的推荐用法。
// 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;
}
没有程序参数的示例输出:
loop #1
程序停止响应,而不执行进一步的迭代。
具有程序参数:
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
说明
以下示例说明了 _resetstkoflw
在结构化异常转换为 C++ 异常的程序中的推荐用法。
代码
// 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.