24 de octubre de 2007

Error Confuso con la Memoria

Lo peor que le puede pasar a un programador un viernes por la tarde es encontrar un nuevo error en el código en el que este trabajando. Si tiene suerte, lo podrá resolver en poco tiempo y marchara a casa tranquilo. Si no tiene tanta suerte, ese viernes saldrá tarde. Y si no tiene nada de suerte, además de salir tarde un viernes, dormirá mal ese día, y seguramente vaya el sábado a intentarlo arreglar.

Un viernes me toco a mi echarme a temblar. Faltaba media hora para que me marchase y se me acerca un compañero programador:

el otro: Oye, que me peta el programa en tu código.

yo: Bueno, vamos a ver que pasa. ¿Y en que parte de mi código?¿Por donde se iba ejecutando el programa?

el otro: Bueno.. er..esto.. en una función nueva que estoy haciendo. - Yo empezaba a ver un hueco por el que librarme.- Es que en algún lugar al principio de mi función peta sin más y el código que muestra en la pila de llamadas es una función tuya.

yo: Pero, lo que es mi función... Ni siquiera la llamas desde tu código.

el otro: Pues no. - Vamos, con un hueco así se libra hasta el más torpe.

yo: Vale. Pues algo haces en tu código, que corrompe la memoria de la pila de llamadas y después de petar casualmente se cree que el código causante del fallo es el mio.

el otro: Bueno, si. Puede ser eso. Lo mirare.

De buena me libre. Como tenía tiempo, y no tenía ninguna atadura moral en caso de no encontrar nada, me puse a mirar su código.

void FunctionF( void ){
MyStruct data;

FunctionG( loquesea );

// ...
}

El programa termina justo antes de entrar en la FunctionG por un acceso ilegal a memoria. Un primer vistazo, parece que los datos en la pila están corruptos. ¿Que es lo que ha podido causar la corrupción? Pues parece que el único sitio es la declaración de la variable data. Si vamos a ver que pinta tiene MyStruct, nos encontramos con lo siguiente.

const MAX_ARRAY = 128;

struct Element
{
int array1[ MAX_ARRAY ];
int array2[ MAX_ARRAY ];
int array3[ MAX_ARRAY ];
int array4[ MAX_ARRAY ];

unsigned int index1;
unsigned int index2;
unsigned int index3;
unsigned int index4;
};

const int NUM_ELEMENTS = 10;

struct MyStruct
{
Element elemens[ NUM_ELEMENTS ];
int posBest;
int posWort;
int posMean;
};

Un pequeño cálculo mental nos da que una variable de tipo MyStruct ocupa más de 20 kilobytes. Y esa es la solución al problema. La declaración de la variable data, llena todo el espacio de memoria dinámica en la pila, machacando en el proceso la pila de llamadas. Por lo que cualquier llamada a una función posterior causara una excepción.

Mi solución
void FunctionF( void )
{
MyStruct * data = new MyStruct;
assert( data );

// ...

delete data;
}

Bueno, pues la historia tuvo final feliz. Se cambio la inicialización de la variable y nos pudimos ir todos ese viernes con la conciencia tranquila a casa.

2 comentarios:

Marc Ordinas i Llopis dijo...

Hala! Y en teoría una de las ventajas de C++ es que puedes meter datos en la pila, no?

Luis Cabellos dijo...

Más tarde me dio por mirar el tamaño disponible en la pila, y creo recordar que ran 5 kilobytes. Si en teoría puedes meter datos en la pila, siempre que no te pases con el tamaño :-D