【问题标题】:Without using recursion how can a stack overflow exception be thrown?不使用递归如何抛出堆栈溢出异常?
【发布时间】:2010-12-07 16:43:27
【问题描述】:

不使用递归如何抛出堆栈溢出异常?

【问题讨论】:

  • 抱歉重复的问题正文,但我想不出任何值得添加的内容。
  • 没关系。我经常发现自己处于同样的状态:)
  • 你总是可以把nt(无文本)放在正文中。这在 BBS 时代总是有效的。
  • stackoverflow 至少需要 15 个字符。
  • @Anthony Mills - 我认为这行不通(我认为问题正文中要求的字符数最少)而且我认为这甚至不应该被视为一种好习惯如果它确实有效。

标签: exception recursion stack stack-overflow


【解决方案1】:

因为没有人提到它:

throw new System.StackOverflowException();

您可以在测试或进行故障注入时这样做。

【讨论】:

  • 很好 - 假设您使用的是 .NET =)
【解决方案2】:

将一个 ENORMOUS 数组声明为局部变量。

【讨论】:

  • 大多数编译器根本不会编译它。这不是堆栈溢出。
  • @Chris - 不会编译它?我认为最大堆栈大小是由链接器设置的,编译器不知道。
  • 编译器无法捕捉到它,除非编译器能够分析代码以预测运行时堆栈的使用情况,这可能非常棘手。
  • 是的...根据我的经验,这通常是导致堆栈溢出的原因。通常它是用于某种消息处理的巨大本地 char[] 缓冲区。
【解决方案3】:

如果你调用了足够多的方法,堆栈溢出随时可能发生。虽然,如果您在不使用递归的情况下遇到堆栈溢出错误,您可能需要重新考虑您是如何做事的。使用递归非常简单,因为在无限循环中,您会调用大量方法。

【讨论】:

    【解决方案4】:

    以下内容适用于 Windows,但大多数操作系统都以类似的方式实现这一点。

    简短的回答是:如果你触摸最后一个保护页面,它会抛出。

    当您的应用程序触及堆栈的底部页面时引发类型为 EXCEPTION_STACK_OVERFLOW (C00000FD) 的异常,该页面标记为PAGE_GUARD 保护标志,并且没有空间增长堆栈(再提交一页),见How to trap stack overflow in a Visual C++ application
    发生这种情况的典型情况是由于堆栈上的许多函数帧(即失控递归)导致堆栈增长,这是由于帧较少但帧大小非常大(具有非常大的局部作用域的函数)对象)或通过使用_alloca从堆栈显式分配。
    导致异常的另一种方法是简单地故意触摸保护页面,例如。通过取消引用指向该页面的指针。这可能是由于变量初始化错误造成的。

    如果输入导致非常深的嵌套级别,则有效执行路径上可能会发生堆栈溢出。例如见Stack overflow occurs when you run a query that contains a large number of arguments inside an IN or a NOT IN clause in SQL Server.

    【讨论】:

      【解决方案5】:

      每个尚未返回的方法调用都会消耗一些堆栈空间。 (具有更多局部变量的方法会占用更多空间。)非常深的调用堆栈会导致堆栈溢出。

      请注意,在内存有限的系统(移动设备等)上,您没有太多堆栈空间,而且很快就会用完。

      【讨论】:

      • 我在一个控制台项目上工作,我们的进程有 32K 堆栈。在其中一个例程中,有两个 16K 数组。尽管数组的使用是排他性的并且它们不在同一范围内,但编译器仍然分配了 32K 的堆栈空间并溢出了我们的堆栈(理论上更智能的编译器只会保留 16K)。我将它们都更改为 alloc/free 以解决问题。
      【解决方案6】:

      简短的回答:如果您有一个调用内部对象的对象,则将堆栈跟踪增加 1。因此,如果您有 1000 个相互嵌套的对象,每个对象都调用其内部对象,最终您会得到一个堆栈溢出。

      这里演示了如何使用嵌套迭代器生成素数:

      using System;
      using System.Collections.Generic;
      using System.Text;
      
      namespace ConsoleApplication1
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Program p = new Program();
      
                  IEnumerator<int> primes = p.AllPrimes().GetEnumerator();
                  int numberOfPrimes = 1000;
                  for (int i = 0; i <= numberOfPrimes; i++)
                  {
                      primes.MoveNext();
                      if (i % 1000 == 0)
                      {
                          Console.WriteLine(primes.Current);
                      }
                  }
                  Console.ReadKey(true);
              }
      
              IEnumerable<int> FilterDivisors(IEnumerator<int> seq, int num)
              {
                  while (true)
                  {
                      int current = seq.Current;
                      if (current % num != 0)
                      {
                          yield return current;
                      }
                      seq.MoveNext();
                  }
              }
      
              IEnumerable<int> AllIntegers()
              {
                  int i = 2;
                  while (true)
                  {
                      yield return i++;
                  }
              }
      
              IEnumerable<int> AllPrimes()
              {
                  IEnumerator<int> nums = AllIntegers().GetEnumerator();
                  while (true)
                  {
                      nums.MoveNext();
                      int prime = nums.Current;
                      yield return prime;
      
                      // nested iterator makes a big boom     
                      nums = FilterDivisors(nums, prime).GetEnumerator();
                  }
              }
          }
      }
      

      没有递归,但程序会在大约 150,000 个素数后抛出堆栈溢出异常。

      【讨论】:

      • ncie 代码,让我想起 Haskell 程序员的进化 :)(针对 oneliner 的大量代码 - 编写阶乘)
      【解决方案7】:

      如果您在谈论具有合理标准库的 C++,我认为这会起作用:

      while (true) {
          alloca(1024 * 1024); // arbitrary - 1M per iteration.
      }
      

      alloca 的详细信息。

      【讨论】:

      • 这是堆栈溢出还是内存不足异常?
      • @juliet:alloca()函数在调用者的栈帧中分配空间。
      • 如果 alloca() 被正确实现,这不应该抛出异常。如果没有足够的堆栈空间,调用 alloca() 应该返回 NULL 而不是抛出异常。应该发生的是,在您用完堆栈空间后,您的代码将陷入 alloca() 调用返回 NULL 的无限循环中。
      • 来自您答案中的链接:返回值 - 如果内存不足,alloca() 函数将返回一个空指针。
      • 可能想将循环更改为while(true) { char *c = alloca(1024 * 1024); c[1024 * 1024 - 1] = 0; }(虽然这是一个段错误,而不是堆栈溢出,但在我的系统上代码int main(void) { return main(); }(编译为C)段错误)。
      【解决方案8】:
      int main()
      {
        //something on the stack
        int foo = 0;
        for (
          //pointer to an address on the stack
          int* p = &foo;
          //forever
          ;
          //ever lower on the stack (assuming that the stack grows downwards)
          --p)
        {
          //write to the stack
          *p = 42;
        }
      }
      

      【讨论】:

        【解决方案9】:

        您也可以在堆栈中分配几个字节。

        static void Main(string[] args)
        {
            Span<byte> b = stackalloc byte[1024 * 1024 * 1024]; // Process is terminating due to StackOverflowException.
        }
        

        【讨论】:

          【解决方案10】:

          产生 StackOverflowException 的最简单方法如下:

          using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Text;
          
          namespace ConsoleApplication2
          {
              class Program
              {
                  static void Main(string[] args)
                  {
                      SomeClass instance = new SomeClass();
                      string name = instance.Name;
                  }
              }
          
              public class SomeClass
              {
                  public string Name
                  {
                      get
                      {
                          return Name;
                      }
                  }
              }
          }
          

          【讨论】:

          • 实际上这是抛出异常后的第二个最简单的方法:)
          • 该问题专门排除了递归。
          猜你喜欢
          • 2016-02-19
          • 1970-01-01
          • 2016-03-07
          • 1970-01-01
          • 2018-03-25
          • 2017-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多