[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