2012-08-27 29 views
18

En C++, un desbordamiento de pila generalmente provoca un bloqueo irrecuperable del programa. Para los programas que necesitan ser realmente robustos, este es un comportamiento inaceptable, particularmente porque el tamaño de la pila es limitado. Algunas preguntas sobre cómo manejar el problema.Cómo controlar o evitar un desbordamiento de pila en C++

  1. Hay una manera de evitar el desbordamiento de la pila mediante una técnica general. (Una solución escalable y robusta que incluye tratar con librerías externas que consumen mucha pila, etc.)

  2. ¿Hay alguna manera de manejar los desbordamientos de pila en caso de que ocurran? Preferiblemente, la pila se desenrolla hasta que hay un controlador para hacer frente a ese tipo de problema.

  3. Hay idiomas que tienen subprocesos con pilas expandibles. ¿Algo así es posible en C++?

Cualquier otro comentario útil sobre la solución del comportamiento C++ sería apreciado.

+4

http://support.microsoft.com/kb/315937 –

+1

El estándar ni siquiera menciona la pila, se debe especificar la plataforma que se dirigen; varias plataformas proporcionan medios para interceptar el desbordamiento de la pila o incluso obtener una "alerta de desbordamiento de la pila" cuando la pila está casi agotada. –

+5

Personalmente, encuentro que SO no es algo para evitar, sino para abrazar. ¡Solo mira la gran comunidad aquí! –

Respuesta

17

Manejar un desbordamiento de pila no es la solución correcta, en su lugar, debe asegurarse de que su programa no desborde la pila.

No asigne variables grandes en la pila (donde lo que es "grande" depende del programa). Asegúrese de que cualquier algoritmo recursivo termine después de una profundidad máxima conocida. Si un algoritmo recursivo puede recurrir un número desconocido de veces o una gran cantidad de veces, puede administrar la recursión usted mismo (manteniendo su propia pila asignada dinámicamente) o transformar el algoritmo recursivo en un algoritmo iterativo equivalente

Un programa que debe ser "realmente robusto" no utilizará bibliotecas de terceros o externas que "consuman mucha pila".


Tenga en cuenta que algunas plataformas notifican un programa cuando se produce un desbordamiento de pila y permiten que el programa maneje el error. En Windows, por ejemplo, se lanza una excepción. Sin embargo, esta excepción no es una excepción de C++, es una excepción asincrónica. Mientras que una excepción de C++ solo puede lanzarse mediante una instrucción throw, se puede lanzar una excepción asíncrona en cualquier momento durante la ejecución de un programa. Sin embargo, esto se espera porque un desbordamiento de pila puede ocurrir en cualquier momento: cualquier llamada de función o asignación de pila puede desbordar la pila.

El problema es que un desbordamiento de pila puede provocar una excepción asíncrona incluso desde el código que no espera arrojar ninguna excepción (por ejemplo, desde funciones marcadas noexcept o throw() en C++). Entonces, incluso si maneja esta excepción de alguna manera, no tiene forma de saber que su programa está en un estado seguro. Por lo tanto, la mejor manera de manejar una excepción asincrónica es no manejarla en absoluto (*). Si uno es arrojado, significa que el programa contiene un error.

Otras plataformas pueden tener métodos similares para "manejar" un error de desbordamiento de pila, pero cualquiera de estos métodos es probable que sufra el mismo problema: el código que se espera que no cause un error puede causar un error.

(*) Hay algunas excepciones muy raras.

+0

El uso de bibliotecas de terceros a menudo es una cuestión de costos. Tienes que usar WinApi directa o indirectamente si estás en Windows en tu programa en algún momento. Lo mismo en otro sistema. No podrá estar sin bibliotecas de terceros en absoluto. (Puede considerar la biblioteca estándar de C++ como un tercero). Pero mi punto es que, si desea tener un programa súper robusto y usar bibliotecas de terceros para reducir los costos, entonces podría necesitar una forma de garantizar que el programa no lo haga. bloqueo completo por un desbordamiento de pila en otras bibliotecas. –

0

C++ es un lenguaje poderoso, y con ese poder viene la capacidad de dispararse en el pie. No estoy al tanto de ningún mecanismo portátil para detectar y corregir/abortar cuando ocurre el desbordamiento de la pila.Ciertamente, cualquier detección de este tipo sería específica de la implementación. Por ejemplo, g ++ proporciona -fstack-protector para ayudar a controlar el uso de la pila.

En general, su mejor opción es ser proactivo al evitar grandes variables basadas en la pila y tener cuidado con las llamadas recursivas.

+0

'-fstack-protector' no ayuda a controlar el exceso de asignación de pila. Es para detectar cuándo las variables asignadas a la pila escriben fuera de sus límites. –

3

Puede proteger contra desbordamientos de pila usando buenas prácticas de programación, como:

  1. Tenga mucho cuidado con la recursividad, he visto recientemente un resultado SO de la función CreateDirectory recursiva mal escrito, si no está seguro de si su el código es 100% correcto, luego agrega la variable de protección que detendrá la ejecución después de N llamadas recursivas. O aún mejor, no escriba funciones recursivas.
  2. No cree matrices enormes en la pila, esto podría ser matrices ocultas como una matriz muy grande como un campo de clase. Siempre es mejor usar el vector.
  3. Tenga mucho cuidado con alloca, especialmente si se coloca en alguna definición de macro. He visto numerosos SO resultantes de las macros de conversión de cadenas puestas en bucles que usaban alloca para asignaciones rápidas de memoria.
  4. Asegúrate de que el tamaño de tu pila sea óptimo, esto es más importante en las plataformas embebidas. Si el hilo no hace mucho, déjalo en una pila pequeña; de lo contrario, utilice un tamaño mayor. Sé que la reserva solo debería tomar un rango de direcciones, no la memoria física.

esas son las causas más SO que he visto en los últimos años.

Para la búsqueda automática de SO, debe ser capaz de encontrar algunas herramientas de análisis de código estático.

+0

Gracias por estas ideas constructivas. –

0

Re: pilas expandibles. Se podría disponer de más espacio en la pila con algo como esto:

#include <iostream> 

int main() 
{ 
    int sp=0; 

    // you probably want this a lot larger 
    int *mystack = new int[64*1024]; 
    int *top = (mystack + 64*1024); 

    // Save SP and set SP to our newly created 
    // stack frame 
    __asm__ ( 
     "mov %%esp,%%eax; mov %%ebx,%%esp": 
     "=a"(sp) 
     :"b"(top) 
     : 
     ); 
    std::cout << "sp=" << sp << std::endl; 

    // call bad code here 

    // restore old SP so we can return to OS 
    __asm__(
     "mov %%eax,%%esp": 
     : 
     "a"(sp) 
     :); 

    std::cout << "Done." << std::endl; 

    delete [] mystack; 
    return 0; 
} 

Ésta es la sintaxis de ensamblador de gcc.

+0

Vaya, haga que 'int * top = (mystack + 64 * 1024 - 1);' –

+0

Probablemente también re: suficiente potencia para dispararse en el pie. –

+0

¡Ay! ¡Qué hack! –

-2
#include <iostream> 
using **namespace** std; 
class Complex 
{ 

public: double *re, *im; 

Complex() 
{ 

    re = new double(r); 

    im = new double(m); 

} 

Complex() 
{ 

    re = new double; 
    im = new double; 
    *re = *t.re; 
    *im= *t.im; 

} 

~Complex() 
{ 

     delete re, im; 

} 

}; 

int main() { 

double x, y, z; 
cin >> x >> y >> z; 
Complex n1(x,y); 
cout << *n1.re << "+" << *n1.im << "i "; 
Complex n2 = n1; 
cout << *n2.re << "+" << *n2.im << "i "; 
*n1.im = z; 
cout << *n2.re << "+" << *n2.im << "i "; 
cout << *n1.re << "+" << *n1.im << "i "; 
return 0; 
} 
+1

¿Cómo se relaciona esto con la pregunta? –

Cuestiones relacionadas