_resetstkoflw

从堆栈溢出恢复。

重要

此 API 不能用于在 Windows 运行时中执行的应用程序。 有关详细信息,请参阅通用 Windows 平台应用中不支持的 CRT 函数

语法

int _resetstkoflw( void );

返回值

如果函数成功,则为非零值;如果失败,则为 0。

备注

_resetstkoflw 函数将从堆栈溢出条件中恢复,让程序继续执行,而不会出现致命异常错误。 如果未调用 _resetstkoflw 函数,则在前一个异常后不会显示保护页。 下次发生堆栈溢出时,将不会有任何异常,且进程会在无警告的情况下终止。

如果应用程序中的线程导致 EXCEPTION_STACK_OVERFLOW 异常,则该线程的堆栈将处于损坏状态。 此异常不同于其他异常,例如EXCEPTION_ACCESS_VIOLATIONEXCEPTION_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.

另请参阅

_alloca