Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: iplazlac en 21 Noviembre 2010, 15:19 pm



Título: [C] [?] Función free() y uso posterior
Publicado por: iplazlac en 21 Noviembre 2010, 15:19 pm
Hola, tengo un problemilla con la función free() en c. Tengo una "tabla" con claves y valores , la estructura es la siguiente.

Código
  1. /* Definicion del tipo */
  2. typedef struct {
  3.  size_t capacidad;
  4.  size_t numElementos;
  5.  size_t tamClave;
  6.  size_t tamValor;
  7.  void *claves;
  8.  void *valores;
  9.  FuncionComparacion compara;
  10. } Tabla;
  11.  

Asignacion de memoria:

Código
  1. /* Crea una tabla. El booleano devuelto indica si se pudo crear la tabla */
  2. bool tablaCrea( Tabla * tablaA, size_t capacidad, size_t tamClave, size_t tamValor, FuncionComparacion compara ){
  3.  bool res=true;
  4.  tablaA->capacidad=capacidad;
  5.  tablaA->tamClave=tamClave;
  6.  tablaA->tamValor=tamValor;
  7.  tablaA->compara=compara;
  8.  tablaA->numElementos=0;
  9.  tablaA->claves=(void *) malloc (tamClave*capacidad);
  10.  tablaA->valores=(void *) malloc (tamValor*capacidad);
  11.  if(tablaA->claves== NULL || tablaA->valores==NULL ||compara==NULL||capacidad==0||tamClave==0||tamValor==0||tablaA==NULL)
  12.    res=false;
  13.  return res;
  14. }
  15.  

Vale, ya tengo mi tabla con sus bloques de memoria reservada para las claves y valores, hasta ahora todo correcto. El problema viene cuando libero la tabla, tengo tambien otra funcion para verificar si la tabla es o no válida. Os las adjunto ambas.

Código
  1. /* Indica si la tabla es o no valida */
  2. bool tablaEsValida( Tabla *tablaA ){
  3.  return (tablaA!=NULL);
  4. }
  5.  
  6. /* Libera la tabla.  El valor devuelto indica si la operación se ha podido
  7.    realizar */
  8. bool tablaLibera( Tabla *tablaA ){
  9.  if(tablaA!=NULL){
  10.    free(tablaA->claves);
  11.    tablaA->claves=NULL;
  12.    free(tablaA->valores);
  13.    tablaA->valores=NULL;
  14.    free(tablaA);
  15.    tablaA=NULL;
  16.    return true;
  17.  }else{
  18.    return false;
  19.  }
  20. }
  21.  

Lo que se de free() hasta ahora:
- Un programador prudente haria :
free(puntero);
puntero=NULL;
Por si se da un acceso posterior al free(), el comportamiento no estaría definido.
-free() no devuelve nada.
-Si haces free() de un puntero nulo sencillamente no hace nada, no hay problema.
Hasta aqui yo creo que esta todo correcto, pero al hacer la siguiente comprobación:

Código
  1.    /* Liberamos la tabla y comprobamos que deja de ser válida*/
  2.    tablaLibera( &tablaIntInt );
  3.    assert( !tablaEsValida( &tablaIntInt ) );
  4.  
El assert falla, parece ser que despues de liberar a tablaEsValida() no le llega como NULL. Tabla deberia salir NULL de liberaTabla() pero no se muy bien como no le llega como NULL a tablaEsValida().
Un saludo y mucha gracias por vuestro tiempo por adelantado.
PD. No se si me he pasado de codigo o datos, pero prefiero que tengais informacion de sobra que no que falte.


Título: Re: [C] [?] Función free() y uso posterior
Publicado por: do-while en 21 Noviembre 2010, 17:58 pm
ˇBuenas!

El problema esta en que a la funcion que libera los datos le pasas un puntero a Tabla. A efetos practicos, ese puntero es como si fuese una variable local, por lo tanto todos los cambios que realices sobre el puntero (no sobre el objeto al que apunta), solo afectaran a esa "variable local" y no al objeto al que apunta. Es decir, si haces que ese puntero se NULL, no haras que un puntero exterior que hubieses puesto en la lista de parametros sea NULL. Recuerda el problema del intercambio:

Código
  1.  
  2. void intercambio(int *a,int *b) //para poder modificar enteros exteriores usamos punteros
  3. {
  4.    int aux;
  5.  
  6.    aux = *a;
  7.    *a = *b;
  8.    *b = aux;
  9. }
  10.  
  11.  

Lo mismo ocurre con los punteros, si quieres modificar un puntero exterior a la funcion, deberas pasar un puntero a ese puntero, y luegoya trabajar con el, asi que suponiendo que el reto de tu codigo este bien, y que a la funcion liberar le pases un puntero, tendras que cambiar la llamada de la funcion para pasar un puntero a ese puntero y cambiar el codigo dentro de la funcion asi:

Código
  1. bool tablaLibera( Tabla **tablaA ){
  2.  if(*tablaA != NULL){
  3.    free((*tablaA)->claves);
  4.    (*tablaA)->claves=NULL;
  5.    free((*tablaA)->valores);
  6.    (*tablaA)->valores=NULL;
  7.    free((*tablaA)); //Ahora si que estas modificando un puntero exterior a la funcion
  8.    (*tablaA)=NULL;//Ahora si que estas modificando un puntero exterior a la funcion
  9.    return true;
  10.  }else{
  11.    return false;
  12.  }
  13. }
  14.  


Como ves, el funcionamiento de las dos funciones es similar. En una desreferenciamos un entero, y en la segunda desreferenciamos un puntero a un puntero a Tabla, asi en la primera modificamos un entero exterior a la funcion, y en la segunda modificamos un puntero a Tabla exterior a la funcion.

ˇSaludos!


Título: Re: [C] [?] Función free() y uso posterior
Publicado por: iplazlac en 21 Noviembre 2010, 18:35 pm
Hola do-while.
Muchas gracias por tus explicaciones, me han sido de mucha utilidad.
Problema solucionado.
Un saludo.