Puntero doble

Iniciado por LadyWin, 15 Agosto 2021, 06:30 AM

0 Miembros y 1 Visitante están viendo este tema.

LadyWin

Buenas noches! como andan? queria consultarles algo que me tiene un poco confundida...


typedef struct tDATO {
int DNI;
char * nombre;
} DATO;
typedef struct tCola {
DATO contenido;
struct tCola *sgte;
} * COLA;

int main (void) {
COLA * ppio = inicializar(ppio);


Como 'COLA' es un puntero entonces 'ppio' seria un doble puntero? si no es asi, como seria su correcta definicion?

Utilizo este modo ya que necesito pasar parametros por referencia para que sus datos se modifiquen en los modulos, pero termina siendo medio engorroso a la hora de acceder a sus campos, siempre me salen muchos errores,por ejemplo este me salio sin ningun problema:


COLA * crearCola (DATO * nuevoDato) {
COLA * nuevo_elemento = (COLA*) malloc (sizeof(COLA));
(*nuevo_elemento)->contenido.DNI = nuevoDato->DNI;
strcpy((*nuevo_elemento)->contenido.nombre,nuevoDato->nombre);
(*nuevo_elemento)->sgte = NULL;
return nuevo_elemento;
}


Pero en este no, me salen un monton de errores siendo que los hice de la misma manera:

void agregarAlFinal (COLA * ppio, COLA * nuevoNodo) {
COLA * aux = ppio;
while (!esVacia(aux)) {
if (esVacia(aux->sgte)) //'*aux' is a pointer; did you mean to use '->'?
(*aux)->sgte = nuevoNodo;//warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
         aux = (*aux)->sgte;  //warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
}
}


Si me pueden ayudar seria de gran ayuda, desde ya muchas gracias!  ;-)

MAFUS

Bueno, la verdad es que es te estás liando un poco con los punteros y los dobles punteros.

(*aux)->sgte = nuevoNodo;

Aquí nuevoNodo es COLA * nuevoNodo, lo que es lo mismo, struct tCola ** nuevoNodo (un puntero doble). Pero sgte espera un puntero simple:

typedef struct tCola {
DATO contenido;
struct tCola *sgte;
} * COLA;


En la definición se puede ver que sgte es un puntero simple.

Ídem para aux = (*aux)->sgte;

RayR

Aquí también tienes un error:

(*nuevo_elemento)->contenido.DNI = nuevoDato->DNI;

porque reservaste memoria para nuevo_elemento, pero no para *nuevo_elemento, por lo que este último es un puntero sin inicializar, así que en la línea anterior estás escribiendo en quién sabe que dirección. Antes tienes que hacer algo como esto:

*nuevo_elemento = (COLA)malloc(sizeof(tCola));

De todas formas, piensa bien si de verdad necesitas tantas indirecciones. Puedes pasar variables por puntero/referencia sin usar realmente un puntero, simplemente usando el operador &. Fíjate a ver si puedes simplificar tu programa.

LadyWin

Gracias por responder! Entiendo lo que me dicen, pero entonces no es posible acceder a los campos cuando la sintaxis es hecha de este modo?, aca hice un codigo mas simple, 'ppio' ya no es puntero pero tambien me sale el mismo error:

#include <stdio.h>
#include <stdlib.h>

typedef struct rNodo {
    int numero;
    struct rNodo * siguiente;
} * tNodo;

void inicializar(tNodo * ppio);
void agregarElemento (tNodo * ppio);

int main () {
    tNodo ppio;
    inicializar (&ppio); // Apunta a ppio
    agregarElemento(&ppio);
    printf("%i", ppio->numero );

    return 0;
}
void inicializar(tNodo * ppio) {
    *ppio = NULL;
}
void agregarElemento (tNodo * ppio) { //nuevo es un puntero y, numero y num1 son enteros, pero como tNodo es puntero ahi me genera errores
    int num1 = 23;
    tNodo * nuevo = (struct rNodo*) malloc (sizeof(struct rNodo));  //warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
    nuevo->numero = num1;  //error: '*nuevo' is a pointer; did you mean to use '->'?
    nuevo->siguiente = NULL;  //error: '*nuevo' is a pointer; did you mean to use '->'?
    ppio = nuevo;

}


Lo que quiero evitar es escribir (**) por esta razon quiero buscar otras alternativas, desde ya muchas gracias!
;D

RayR

Si tienes algún manual de C, revisa bien la sección de punteros, porque sigues confundiendo mucho las cosas. A lo que me refiero con mi mensaje anterior es a que cuando tienes una variable ptr que es un puntero a puntero (o puntero doble como dices), debes inicializar tanto a ptr como a *ptr. Para este ejemplo, eso significa que necesitas dos llamadas a malloc, cada una reservando el espacio necesario y haciendo el cast al tipo correspondiente para cada puntero. No estás haciendo ninguna de esas cosas en la línea 25, y además, dado que nuevo es un puntero a puntero, lo estás desreferenciado mal ( (*nuevo)->campo es la manera correcta).  De cualquier forma te estás complicando más de lo necesario, ya que nuevo no necesita ser un puntero a tNodo. Simplemente cambia la línea 25 a:

tNodo nuevo = (tNodo)malloc(sizeof(struct rNodo));

y la 28 a

*ppio = nuevo;

MAFUS

Mejor sería reestructurar los tipos de datos: uno para tener guardar el dato mismo y otro para guardar la cola.

Verás que no se han hecho uso de punteros dobles. Muchas veces se pueden evitar

Dato:typedef struct tDATO {
int DNI;
char * nombre;
struct tDATO *siguiente;
} *DATO;


El dato guarda la información útil más un puntero que hace la lista. En verdad se podría realizar una estructura anidada, la estructura interna con el dato y la externa que contendrá el puntero (pero eso ya más avanzado).

Cola:typedef struct tCola {
    DATO primero;
    DATO ultimo;
    size_t n_elementos;
} *COLA;


La cola nos sirve para trabajar con la estructura cola, nada más. Guarda el primer dato, el último y el número de elementos.

Teniendo ya los dos tipos de datos estructurados vamos a crear la funcionalidad para manejarlos de forma sencilla. A por las funciones:

Para los datos .......

Primero vamos crear el dato

DATO dato_crear(int dni, char *nombre) {
    DATO dato = malloc(sizeof(struct tDATO));
    dato->DNI = dni;
    dato->nombre = malloc((strlen(nombre)+1) * sizeof(char));
    strcpy(dato->nombre, nombre);
    dato->siguiente = NULL;

    return dato;
}


Esto genera un tipo dato en memoria dinámica, nada del otro mundo. Lo único que hay que tener en cuenta es que los campos que se deben alojar en memoria dinámica, como nombre, debe hacerse mediante el uso de *alloc. Esto es especialmente así sobre todo en cadenas que te pueden venir de diferentes formas y no se puede asignar directamente el valor de la variable nombre a dato->nombre por la razón de que te pueden pasar un puntero de un array local y al salir de la función esa memoria sería liberada, es decir, si te hacen esto:

DATO dato_crear(int dni, char *nombre) {
    ...
    dato->nombre = nombre;
    ...
}

void generar_dato() {
    ...
    char nombre[] = "Tirant Lo Blanc";
    dato_crear(dni, nombre);
    ...
}


Repito, lo anterior está mal. Por otra parte se ve que 'siguiente' va a NULL, esto es así para que se puedan encontrar fallos de lógica.

Liberar dato de la memoria

void dato_liberar(DATO dato) {
    free(dato->nombre);
    free(dato);
}


Antes de liberar el dato propiamente dicho hay que liberar la memoria que ha adquirido para su funcionamiento, en este caso la cadena nombre.

Como puedes haber observado este es el funcionamiento básico, no hay guardas para saber si no se han pasado datos NULL o que no hayan ocurrido errores, pero esto es a lo que vamos.

Vamos a por la cola.....

Primero se crea la cola

COLA cola_crear() {
    COLA cola = malloc(sizeof(struct tCola));
    cola->primero = NULL;
    cola->ultimo = NULL;
    cola->n_elementos = 0;

    return cola;
}


Sencillo se genera en memoria dinámica y se ponen todos los elementos a 0 o NULL.

Agregar un dato a la cola

void cola_agregar(COLA cola, DATO dato) {
    if(cola_es_vacia(cola)) {
        cola->primero = dato;
    } else {
        cola->ultimo->siguiente = dato;
    }
    cola->ultimo = dato;
    cola->n_elementos++;
}


Los datos a una cola se introducen por el final, nuestro dato COLA ya tiene un puntero a su último elemento así que el trabajo es sencillo. Lo único que hay que tener en cuenta es si la cola está vacía: si está vacía el elemento introducido no sólo es el último sino que también es el primero; si la cola no está vacía hay que tener presente para el buen funcionamiento de nuestra estructura de datos que el último elemento de la cola apunte al que vamos a agregar.

Extraer un dato de la cola

DATO cola_extraer(COLA cola) {
    DATO dato = cola->primero;

    if(!cola_es_vacia(cola)) {
        cola->primero = cola->primero->siguiente;
        dato->siguiente = NULL;
        if(cola->primero == NULL) {
            cola->ultimo = NULL;
        }
        cola->n_elementos--;
    }

    return dato;
}


Como en una pila los datos se extraen desde e principio. Es importante saber dos cosas: si al principio la cola está vacía por lo que se tendrá que devolver NULL y no hacer nada más; que después de la extracción la cola se vacíe (donde cola->primero tendrá el valor NULL) en ese caso y para evitar problemas lógicos también se lleva a NULL cola->ultimo.

Desalojar la cola de memoria

void cola_liberar(COLA cola) {
    DATO x;
    while(cola->primero) {
        x = cola->primero;
        cola->primero = cola->primero->siguiente;
        dato_liberar(x);
    }
    free(cola);
}


Como con dato primero hay que liberar toda la memoria que haya alojado. Esto se hace recorriendo la lista con cola->primero hasta que sea NULL. Ver que se usa la funcion dato_liberar porque cada dato se debe desalojar usando su propia función (ya que también hace su propia limpieza). Se podrían haber extraído datos con cola_extraer, pero sería más lento.

Después, para evitar trabajar directamente con el dato cola se pueden hacer algunas funciones auxiliares que de hecho ya se han usado en el código anterior, pero esas van más para un usuario de nuestro código:

int cola_es_vacia(COLA cola) {
    return cola->n_elementos == 0;
}

size_t cola_longitud(COLA cola) {
    return cola->n_elementos;
}


LadyWin


Gracias por el tiempo que se tomaron, pude resolver el tema de la sintaxis en la funcion agregarElemento, cree la variable nuevo pero sin puntero, el problema es que en otros modulos me salen otros problemas que no puedo resolverlo por este metodo, probe infinitas formas pero no avanzo, asi que creo que lo mejor sera repasar el tema en mayor profundidad, de igual manera aprendi bastante, gracias nuevamente  ;-) ;-)  ;-)