[C con Clase] Dudas con realloc

Steven Davidson srd4121 en njit.edu
Jue Feb 10 01:44:08 CET 2011


Hola Martín,

On 2/9/2011 4:32 PM, Martin Strahd wrote:
> Buenas
>
> Estoy aprendiendo respecto de la asignación dinamica de memoria en C
> y me encuentro con algunas dificultades a la hora de solucionar un
> ejercicio del manual que estoy siguiendo. El ejercicio en sí pide
> construir un diccionario inglés-español y, para ello, creo que la
> solución consiste en crear un array de punteros a punteros. El
> enunciado del problema es el siguiente:
>

[CORTE]

> Posteo el código completo para explicarme mejor:
>
> #include <stdio.h>
> #include <stdlib.h>
>

Necesitas incluir <string.h> ya que usas varias funciones declaradas en 
este fichero de cabecera.

> /**** Prototipos de las funciones ****/
> int crearDiccionario (char ***, char ***);     /* Recibe dos arrays de
> punteros (el de palabras inglesas y el de palabras españolas) y devuelve
> el número de elementos de ese array */
> void traducir (char**, char **, char *);     /* Recibe los dos arrays de
> punteros y una cadena. Buscará en el array 1 la cadena buscada y si lo
> encuentra devolverá la pareja localizada en el array 2 */
>
> void main(void)

Debería ser,

int main( void )

> {
>      int tam;                        /* En esta variable se almacenará
> el tamaño del array de punteros (es decir, el número de parejas del
> diccionario) */
>      char **ping = NULL, **pesp = NULL;            /* Arrays de punteros
> a punteros char. Uno es el de palabras en ingles y el otro en español */

Aconsejo crear tipos para representar estos conceptos. De lo contrario, 
te puedes hacer un lío muy fácilmente con tantos punteros. Por ejemplo,

typedef struct st_palabra
{
   char *pszPalabra;  /* Cadena dinámicamente adjudicada */
   int nHomologa;  /* Índice a la palabra traducida y asociada a ésta */
} palabra;

typedef struct st_diccionario
{
   palabra *pLista;
   unsigned long int nCantidad;
} diccionario;

Y luego, podemos hacer esto:

diccionario ingles, espannol;

>      char palabra[40];                               /* En esta cadena
> se almacena la palabra a buscar */
>      char *q;                                        /* Almacen para el
> retorno de gets */
>
> tam = crearDiccionario(&ping, &pesp); /* Tras esta función sabremos el
> número de parejas creadas y sus respectivas direcciones */
>                                            /* Los punteros se pasan por
> referencia (&) ya que su valor cambiará durante la ejecución de la
> función y nos interesa conservar esos valores */
>
>      /** Una vez creado el diccionario se consulta sobre él **/
>
>     printf ("\n** Consulta sobre el diccionario **");
>     printf ("\n***********************************");
>     printf ("\nEscribe una palabra para buscar en el diccionario. Pulsa
> <Enter> o EOF para salir de la consulta");
>     printf ("\nPalabra?: ");
>     q = gets(palabra);

No se recomienda usar 'gets()'. Usa 'fgets()' en su lugar, aunque tengas 
que eliminar el carácter '\n' al final, porque puedes indicar la 
cantidad máxima de caracteres a leer y a guardar.

[CORTE]

>             /* Estas dos líneas las pongo por si acaso, aunque el error
> se sigue produciendo */
>             ing = NULL;
>             esp = NULL;
>

Esto es incorrecto. Recuerda que 'ing' y 'esp' apuntan a los punteros 
'ping' y 'pesp' en 'main()'. Si ahora cambias estos parámetros a 
punteros nulos, entonces ya no estás apuntando a los punteros 
originales; de hecho, ya no tendrías nada que ver con los punteros 
originales.

> /*** Reservamos memoria para ambos arrays de punteros ***/
>              ing = (char**)realloc(ing, tam * sizeof (char*));
>              esp = (char**)realloc(esp, tam * sizeof (char*));
>

Aquí obtienes varios errores, porque intentas asignar un 'char **' a un 
'char ***'. Recuerda que C es un lenguaje FUERTEMENTE tipificado; los 
tipos deben concordar.

En primer lugar, debería ser:

ing = (char***)realloc(ing, tam * sizeof (char*));

Sin embargo, esto no es correcto. Recuerda que 'ing' apunta a 'ping' en 
'main()'. Lo que quieres hacer es modificar el valor de 'ping' a través 
de 'ing'. Por lo tanto, debes hacer esto:

*ing = (char**)realloc(*ing, tam * sizeof (char*));
*esp = (char**)realloc(*esp, tam * sizeof (char*));

>
>              /** Ahora se reserva memoria para cada uno de los elementos **/
>              ing[tam-1] = NULL;
>              ing[tam-1] = (char*)realloc(ing[tam-1],
> sizeof(char)*(strlen(palai)+1));
>              esp[tam-1] = NULL;
>              esp[tam-1] = (char*)realloc(esp[tam-1],
> sizeof(char)*(strlen(palae)+1));

Nuevamente, al usar 'ing' y 'esp', debemos acceder al valor apuntado; 
esto es,

(*ing)[tam-1] = (char*)realloc((*ing)[tam-1], strlen(palai)+1);

(*esp)[tam-1] = (char*)realloc((*esp)[tam-1], strlen(palae)+1);

No es necesario escribir 'sizeof(char)' porque SIEMPRE será 1 byte, por 
diseño.

>          }
>      }
>      while (palai[0] != 0 && palae[0] != 0);
>
>      return (tam);
> }
>
> Lo que ocurre es que al compilar esto, me salta un mensaje indicando
> que los punteros son incompatibles. Creo que la clave radica en esos
> tres asteriscos que he puesto, pero no se me ocurre otra forma de
> pasar unos punteros que se vayan ajustando al tamaño del array según
> va creciendo, ya que si los paso por valor, al finalizar la función
> crearDiccionario se destruirán y el código no habrá valido de nada.
> ¿Alguna sugerencia?
>

En primer lugar, no aconsejo pensar que se pasa la información por 
referencia, en un lenguaje como C, ya que éste no ofrece tal 
característica. Puedes pensar que C emula el comportamiento de pasar 
información por referencias a través del uso de punteros. La cuestión es 
que usas un puntero a un puntero doble. Si lo prefieres, puedes pensar 
que cada vez que uses 'ing', estás escribiendo '&ping'. Por ejemplo, al 
escribir,

ing = (char**)realloc(ing, tam * sizeof (char*));

es como si estuvieras escribiendo,

&ping = (char**)realloc(&ping, tam * sizeof (char*));

lo cual no tiene sentido. Sin embargo, si escribes esto,

*ing = (char**)realloc(*ing, tam * sizeof (char*));

es equivalente a escribir:

*&ping = (char**)realloc(*&ping, tam * sizeof (char*));

que viene a ser lo mismo que,

ping = (char**)realloc(ping, tam * sizeof (char*));

Esto es justo lo que queremos hacer - o al menos esto es lo que tengo 
entendido según tu código fuente.


Espero haber aclarado las dudas.

Steven





Más información sobre la lista de distribución Cconclase