Problema basico con listas en C

Iniciado por HardForo, 13 Abril 2016, 23:23 PM

0 Miembros y 3 Visitantes están viendo este tema.

MAFUS

Muy buenas. Muchas gracias, me alegro que mis aportaciones te sirvan.

typedef, que supongo que no sabes que es, sirve para crear alias de tipos de datos. De forma simple
typedef int entero;
ahora escribir entero es como si escribieramos int por lo que
entero num;
es lo mismo que escribir
int num;

Del mismo modo se pueden crear datos de tipo puntero
typedef int *puntero_a_int
ahora puntero_a_int se ha convertido en un int *
por lo que el siguiente fragmento es válido

typedef int *puntero_a_int;
int a = 3;
puntero_a_int = &a;


Ahora al código
typedef struct nodo_t {
    int valor;
    struct nodo_t* siguiente;
} nodo, *pila;


lo que hace es crear un nodo básico en el empleo de listas y pilas:

struct nodo_t {
    int valor;
    struct nodo_t* siguiente;
}


Podría trabajar solo con eso, sustituyendo lo que en el código aparece como nodo por struct nodo_t y lista por struct nodo_t * pero creando esos dos alias el código queda más comprensible a simple vista.

Básicamente lista y nodo son casi lo mismo, uno es un puntero y el otro la estructura. Si sigues el código verás que con esos alias el código es más fácil de seguir.

Para ver como quedaría de la otra forma haz una copia del código y sustituye lista por struct nodo_t* y nodo por struct nodo_t y verás la diferencia.

HardForo

#11
@MAFUS:  nuevamente gracias..... la verdad es que pensaba que typedef era un alias de struct porque siempre lo vi en ese contexto, aprendí un par de cosas más (mil gracias)

He intentado re-escribir el programa original con lo que me has enseñado pero sigo sin poder usar el doble puntero:


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


typedef struct nodo_t {
int value;
struct nodo_t* next;
} node, *lista, **listaPTR;


lista new_node(int value){
node * n = (node *) malloc(sizeof(node));
n->value = value;
n->next = NULL;
return n;
}

lista new_lista(){
return NULL;
}


void print_list(lista head){
node *prev;

prev =  head;
while (prev)
{
printf("%d\n", prev->value);
prev = prev->next;
}
}


void push(lista list, int value)
{
node *e,*prev,*n;
int v;

// camino hasta el final de la list (poco eficiente, mejor tener puntero al final)
e = list;
while (e){
prev = e;
e = e->next;
}

// creo nodo
n = new_node(value);

if (list == NULL)
{
list = n;
//printf("Elem: %d\n",list->value);
//printf("Elementos: %d\n",count(list));
}
else
// inserto al final
prev->next = n;
}


int main() {  


lista l = new_lista();

push(l,20);
push(l,40);

print_list(l);

}


He intentado estos reemplazos:


void push(listaPTR listp, int value)   // aqui
{
node *e,*prev,*n;
int v;

e = &listp;  // aqui



Pero no me funciona...... y realmente necesito entender en que estoy fallando en mi intento y verlo funcionar ya que estoy muy novato.


Nuevamente gracias!
HardForo:  foro de Hardware y programación

Se buscan Mods y colaboradores *

MAFUS

Son fallos básicos de punteros pero a los que se pierde rápidamente la pista cuándo no se está acostumbrado. Con práctica los irás solucionando.

Tienes razón al escribir
void push(listaPTR listp, int value)
pues necesitas variar la dirección contenida en lista si esta apunta a NULL, pero fallas cuándo escribes e = &listp;
por el hecho de que ahora e apunta a la dirección de listp.

*** Dando una vuelta de tuerca al tratamiento de la memoria en C ***
listp debe considerarse una variable local de la función push por lo que obtener su dirección no hará que apuntes a la lista original sino a una parte de la pila de la función a la que estás.
Lo que debes obtener es el valor que hay guardado en listp que será la dirección en la que se guarda la lista.
Sé que es complicado dicho así, pero si lo llegas a entender se acabarán los problemas con los punteros. Como regla nemotécnica piensa en que vas a sacar un asterisco a tu puntero. Algo así:
Tu tienes **listaPTR
Dentro tienes *lista
Como ves hay que quitarse un asterisco de encima para obtener *lista.
En el código
node *e
haces un puntero a node. Es lo mismo que hacer
lista e
ya que node * es lo mismo que lista.
Ahora viene cuando sacamos un asterisco **listaPTR. Para ello, durante la asignación cogemos ese asterisco, lo sacamos de **listaPTR y lo dejamos a su izquierda. En código queda así:
e = *list
Ahora los dos objetos son del mismo tipo y se respeta la igualdad.

Bien. Pasemos a los argumentos de llamada de la función push.
Hemos creado nuestra lista, l, y su tipo es *lista pero el argumento espera que le entreguemos la dirección de memoria donde se guarda ésta. Otra vez podemos hacer uso de una regla nemotécnica: debemos pensar que queremos incluir un asterisco a esa variable y para ello cogemos y, ni cortos ni perezosos, se lo escribimos a mano con el símbolo & a su izquierda. De hecho & parece como si quisiéramos escribir un asterisco de forma rápida y sin levantar el lápiz del papel.
En código queda así:

push(&l,20);

Así *lista con un asterisco más digievoluciona a **listaPTR.

Tu código funcional quedaría de la siguiente forma:
#include <stdlib.h>
#include <stdio.h>


typedef struct nodo_t {
    int value;
    struct nodo_t* next;
} node, *lista, **listaPTR;


lista new_node(int value){
    node * n = (node *) malloc(sizeof(node));
    n->value = value;
    n->next = NULL;
    return n;
}

lista new_lista(){
    return NULL;
}


void print_list(lista head){
    node *prev;

    prev =  head;
    while (prev)
    {
        printf("%d\n", prev->value);
        prev = prev->next;
    }
}


void push(listaPTR list, int value)
{
    node *e,*prev,*n;
    int v; /* v no se usa */

    // camino hasta el final de la list (poco eficiente, mejor tener puntero al final)
    e = *list;
    while (e){
        prev = e;
        e = e->next;
    }

    // creo nodo
    n = new_node(value);

    if (*list == NULL)
    {
        *list = n;
        //printf("Elem: %d\n",list->value);
        //printf("Elementos: %d\n",count(list));
    }
    else
        // inserto al final
        prev->next = n;
}


int main() {
    lista l = new_lista();
    push(&l,20);
    push(&l,40);
    print_list(l);
}

HardForo

#13
Me funcionó perfecto y lo entendí  ;-)

Como conclusión adicional saco que:


pp = &p;  
*pp = p;


Muchisisisimas gracias!


PD: como lista es lo mismo que *nodo, hice unos cambios minimos en el typedef struct:


typedef struct node_t {
int value;
struct node_t* next;
} nodo, *nodoP, *lista, **nodoPP, **listaP;


HardForo:  foro de Hardware y programación

Se buscan Mods y colaboradores *

MAFUS

Muy bien, veo que lo has entendido.
Ahora recuerda que es mala práctica abusar de typedef, y más si typedef esconde detrás punteros. Debe usarse para esclarecer el código, no ofuscarlo. Muchas veces es mejor, para un tercero, saber que un tipo de dato es un puntero; a no ser que sea intención tuya una ocultación expresa de los interiores del tipo de dato en cuestión, simulando a lo que se hace con la programación orientada a objetos.