2009-10-17 5 views
6

Sans utiliser la récursivité, comment une exception de débordement de pile peut-elle être levée? Déclarer une matrice ENORMOUS comme variable locale.Sans utiliser la récursivité, comment une exception de dépassement de pile peut-elle être levée?

+0

Désolé pour le corps de question répétitive, mais je ne pouvais penser à rien qui vaille à ajouter. – JaredCacurak

+0

C'est bon. Je me suis souvent retrouvé dans la même situation :) – pierrotlefou

+0

Vous pouvez toujours mettre nt (pas de texte) dans le corps. Cela a toujours fonctionné dans les jours de BBS. –

Répondre

12

Si vous appelez des méthodes assez, un débordement de pile peut se produire à tout moment. Bien que, si vous obtenez des erreurs de débordement de pile sans utiliser la récursivité, vous pouvez vouloir repenser comment vous faites des choses. C'est tellement simple avec la récursion car dans une boucle infinie, vous appelez une tonne de méthodes.

16

+0

La plupart des compilateurs ne compileront simplement pas ceci. Ce n'est pas un débordement de pile. –

+1

@Chris - Ne le compilera-t-il pas? Je pensais que la taille maximale de la pile était définie par l'éditeur de liens et non connue du compilateur. – ChrisW

+2

le compilateur ne peut pas l'attraper, sauf si le compilateur est capable d'analyser le code pour l'utilisation de la pile d'exécution projetée, ce qui pourrait être extrêmement délicat. – JustJeff

2

Chaque appel de méthode qui n'a pas encore retourné consomme un peu d'espace de pile. (Les méthodes avec plus de variables locales consomment plus d'espace.) Une pile d'appels très profonde peut entraîner un débordement de pile.

Notez que sur les systèmes à mémoire limitée (appareils mobiles et autres) vous n'avez pas beaucoup d'espace de pile et courrez plus tôt.

+1

J'ai travaillé sur un projet de console où nos processus avaient des piles de 32K. Dans l'une des routines, il y avait deux tableaux 16K. Bien que l'utilisation des tableaux soit exclusive et qu'ils n'aient pas la même portée, le compilateur a toujours alloué 32 Ko d'espace de pile et a débordé notre pile (théoriquement, un compilateur plus intelligent n'aurait réservé que 16 Ko). Je les ai changés à alloc/free pour résoudre le problème. – Adisak

20

Comme personne d'autre n'a mentionné:

throw new System.StackOverflowException(); 

Vous pouvez le faire lors du test ou de faire d'injection de faute.

+1

Excellent - en supposant que vous utilisez .NET =) –

1

Si vous parlez C++ avec une bibliothèque standard raisonnable, je l'image que cela fonctionnerait:

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

Détails sur alloca.

+0

Est-ce une exception stackoverflow ou out of memory? – Juliet

+2

@juliet: La fonction alloca() alloue de l'espace dans le cadre de la pile de l'appelant. – pierrotlefou

+2

Cela ne devrait pas déclencher une exception si alloca() est correctement implémentée. L'appel alloca() est censé renvoyer NULL s'il n'y a pas assez d'espace de pile plutôt que de lancer une exception. Ce qui devrait arriver, c'est qu'après avoir épuisé l'espace de la pile, votre code sera bloqué dans une boucle infinie d'appels alloca() retournant des valeurs NULL. – Adisak

2

Réponse courte: si vous avez un objet qui appelle un objet interne, vous augmentez la trace de la pile par 1. Donc, si vous avez 1000s d'objets imbriqués l'un dans l'autre, chacun appelant son objet interne, par la suite vous obtiendrez un débordement de pile.

est ici une démonstration de la façon de générer des nombres premiers en utilisant itérateurs imbriqués:

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(); 
      } 
     } 
    } 
} 

Il n'y a pas récursion, mais le programme lancera une exception de débordement de pile après environ 150 000 nombres premiers.

+0

code ncie, me rappelle de l'évolution du programmeur Haskell :) (tonne de code contre oneliner - programmation d'un factoriel) –

7

Ce qui suit s'applique à Windows, mais la plupart des systèmes d'exploitation implémentent cela de la même manière.

La réponse courte est: si vous touchez la dernière page de garde, il va jeter.

Une exception de type EXCEPTION_STACK_OVERFLOW (C00000FD) est déclenchée lorsque votre application touche la dernière page de la pile, marquée d'un indicateur de protection PAGE_GUARD et qu'il n'y a pas de place pour développer la pile (valider une page supplémentaire), voir How to trap stack overflow in a Visual C++ application.
Le cas typique lorsque cela se produit est lorsque la pile a grandi à la suite de nombreuses fonctions sur la pile (c'est-à-dire hors récursivité), en raison de moins de cadres mais de très grandes tailles (fonctions avec un très grand objet local) ou en attribuant explicitement à partir de la pile avec _alloca.
Une autre façon de provoquer l'exception est de simplement toucher intentionnellement la page de garde, par exemple. en déréférençant un pointeur qui pointe dans cette page. Cela peut arriver en raison d'un bug d'initialisation variable.

débordements de pile peuvent se produire sur les chemins d'exécution valides si l'entrée provoque un niveau d'imbrication très profonde.Par exemple voir 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.

-1

meilleure façon de faire un StackOverflowException est le suivant:

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; 
      } 
     } 
    } 
} 
+1

En fait, c'est la deuxième façon la plus simple après avoir levé l'exception :) –

+3

La question spécifiquement exclu récursion. – meriton

+0

Vous avez raison, mon erreur. –

0
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; 
    } 
} 
Questions connexes