2012-06-18 13 views
6

Al escribir el código C++, aprendí que usar la pila para almacenar memoria es una buena idea.Detectando que la pila está llena

Pero recientemente me encontré con un problema:

tuve un experimento que tenía código que se veía así:

void fun(const unsigned int N) { 
    float data_1[N*N]; 
    float data_2[N*N]; 

    /* Do magic */ 
} 

El código exploted con un fallo seqmentation al azar, y no tenía ni idea por qué.

Resultó que el problema era que estaba tratando de almacenar cosas que eran grandes en mi pila, ¿hay alguna forma de detectar esto? ¿O al menos detectar que ha salido mal?

+1

¿Tiene alguna pregunta sobre C o C++? –

+0

¡Creo que no se puede inicializar una matriz en la pila usando variables en C/C++! –

+0

¿Qué tan grande es su información? Sugiero que almacene datos de 100KB + en montón utilizando malloc/free. (Otras personas pueden sugerir incluso una cota inferior) – nhahtdh

Respuesta

5
float data_1[N*N]; 
float data_2[N*N]; 

Estos son matrices de longitud variable (VLA), como N no es una expresión constante. La const-ness en el parámetro solo asegura que N es de solo lectura. No le dice al compilador que N es una expresión constante.

Los VLA solo están permitidos en C99; en otra versión de C, y todas las versiones de C++ no están permitidas. Sin embargo, algunos compiladores proporcionan VLA como característica de extensión del compilador. Si está compilando con GCC, intente usando la opción -pedantic, le dirá que no está permitido.

ahora por qué su programa da error de segmentación, probablemente a causa de stack-overflow debido al gran valor de N * N:


considerar el uso de std::vector como:

#include <vector> 

void fun(const unsigned int N) 
{ 
    std::vector<float> data_1(N*N); 
    std::vector<float> data_2(N*N); 

    //your code 
} 
+0

Agregué el const a la búsqueda :-) –

+2

el parámetro de la función const no cambia nada aquí, el valor aún se conoce solo en el tiempo de ejecución, por lo que todavía se depende de una extensión del compilador. De todos modos, ya sea C o C++, el punto es que si necesitas mucha memoria, usa heap en su lugar (a través de 'std :: vector'). – Kos

+1

@MartinKristiansen: Edité mi respuesta, diciendo * "La const-ness en el parámetro solo asegura que' N' sea de solo lectura. No le dice al compilador que 'N' es expresión constante." * – Nawaz

1

Trate de usar en su lugar funciona como malloc. Devolverá nulo explícitamente, si no pudo encontrar un bloque de memoria del tamaño que solicitó.

Por supuesto, en ese caso no olvide liberar esta memoria al final de la función, una vez que haya terminado.

Además, puede verificar la configuración de su compilador, con qué límite de memoria de pila genera los binarios.

+2

Esto no es lo que está preguntando. –

+0

Malloc ayuda - y el uso de malloc fue mi solución, pero eso significa que ahora tengo un puntero para seguir. –

+0

Sí, eso es cierto sobre el puntero, pero dado que se encuentra dentro de una sola función, no hay ningún problema al respecto. Simplemente agregue eliminar al final de la función –

1

Una de las razones por las que las personas dicen que es mejor usar la pila en lugar de la memoria del montón puede deberse al hecho de que las variables asignadas en la parte superior de la pila aparecerán automáticamente cuando salga del cuerpo de la función. Para almacenar grandes bloques de información, es habitual utilizar la memoria de pila y otras estructuras de datos como listas vinculadas o árboles. También los recuerdos asignados en la pila son limitados y mucho menos de lo que puede asignar en el espacio de pila. Creo que es mejor administrar la asignación de memoria y liberarla más cuidadosamente en lugar de tratar de usar la pila para almacenar datos grandes.

Puede utilizar la infraestructura que gestiona sus asignaciones de memoria. Además, puede usar VDL para verificar sus pérdidas de memoria y recuerdos que no se liberan.

+0

Ese es mi punto, he escrito el código para que se use "principalmente" en pequeñas "N", pero de vez en cuando una gran N se accure. –

1

¿hay alguna manera de detectar esto?

No, en general.

El tamaño de la pila depende de la plataforma.Normalmente, sistema operativo decide el tamaño de la pila. De modo que puede verificar su sistema operativo (ulimit -s en Linux) para ver cuánta memoria de pila asigna para su programa.

Si su compilador es compatible con stackavail(), puede verificarlo. Es mejor ir a la memoria asignada en el montón en situaciones en las que no está seguro de si excedería el límite de la pila.

+0

pero aquí está la cosa, yo estaba desarrollando en una caja Linux con hardware de alta calidad, y pasé mucho tiempo averiguando por qué mi código no se ejecutaba en mi MacBookPro. La caja de Linux no tenía ningún problema con una pila de asignación de 4 MB, pero a la caja de OSX no le gustaba. –

2

Es extremadamente difícil detectar que la pila esté llena, y para nada portátil. Uno de los mayores problemas es que los marcos de pila son de tamaño variable (especialmente cuando se usan matrices de longitud variable, que en realidad son solo una forma más estándar de hacer lo que las personas estaban haciendo anteriormente con alloca()) por lo que no se pueden usar proxys simples como cantidad de cuadros de pila

Uno de los métodos más simples que es mayoría portátil es poner una variable (probablemente de tipo char de modo que un puntero a que es un char*) a una profundidad conocida en la pila y a continuación, medir la distancia desde que apunta a una variable (del mismo tipo) en el marco de pila actual mediante aritmética de puntero simple. Agregue una estimación de cuánto espacio está a punto de asignar, y puede tener una buena idea de si la pila está a punto de explotar. Los problemas con esto son que no se sabe en qué dirección está creciendo la pila (no, ¡no todos crecen en la misma dirección!) Y calcular el tamaño del espacio de la pila en sí mismo es bastante desordenado (se puede Probar cosas como los límites del sistema, pero son realmente bastante incómodas). Además, el factor de pirateo es muy alto.

El otro truco que he visto aplicar en Windows de 32 bits solamente era tratar de alloca() espacio suficiente y controlar la excepción del sistema que se produciría si había espacio suficiente.

int have_enough_stack_space(void) { 
    int enough_space = 0; 

    __try {   /* Yes, that's got a double-underscore. */ 
     alloca(SOME_VALUE_THAT_MEANS_ENOUGH_SPACE); 
     enough_space = 1; 
    } __except (EXCEPTION_EXECUTE_HANDLER) {} 

    return enough_space; 
} 

Este código es muy no portátil (por ejemplo, no contar con ella trabajando en Windows de 64 bits) y la construcción con gcc mayores requiere un poco de ensamblador en línea desagradable en su lugar! El manejo estructurado de excepciones (de lo que se trata) se encuentra entre las artes negras más negras de Windows. (Y no return desde el interior de la construcción __try.)

+0

Esto se basó todo en el código de comprobación de pila que estaba en la implementación de Tcl 8.4; el código con el uso de SEH se convirtió a usar el primer método basado en puntero en 8.5, y se descartó por completo en 8.6 (que usa una implementación de motor "sin pila", por lo que no necesita casi tanto este tipo de cosas). –

+0

BTW, almacenar las matrices de tamaño variable en el montón será mucho más confiable, especialmente si puede usar técnicas RAII para asegurar una eliminación confiable. –

Cuestiones relacionadas