/
Inicio :: Foros

 F.A.Q.F.A.Q.                  Conéctese para revisar sus mensajesConéctese para revisar sus mensajes   

problema : listas enlazadas

 
      Índice del Foro elrincondelc.com -> Principiantes C/C++
Ver tema anterior :: Ver siguiente tema  
AutorMensaje
polly



Registrado: 19 Jul 2007
Mensajes: 61

MensajePublicado: 19/09/2007 1:43 pm
Título: problema : listas enlazadas

buenas , tengo la siguiente funcion:

Código:

void insertar ( Nodo** cabeza, Item entrada)
{
   Nodo* nuevo;
   nuevo=(Nodo*)malloc(sizeof(Nodo));
   nuevo->dato=entrada;
   nuevo->siguiente=*cabeza;
   
   *cabeza=nuevo;
}

con la estructura Nodo definida asi:

typedef int Item;
typedef struct nodo
{
    Item dato;
    struct nodo* siguiente;
}Nodo;


mi duda es porque se pone (como argumento) Nodo**cabeza y no
Nodo*cabeza ??
se supone que nodo cabeza apunta al primer elemento de la lista y (aunque no lo he puesto)

cabeza->dato = 5;
cabeza->dato=NULL;

luego se hace que el nodo nuevo apunte a cabeza para insertar un nuevo elemento al principio de la lista no ?

de todas formas , podriais ponerme el codigo de :
una lista con un solo elemento int , luego añadir un nuevo elemento a la cabeza ,,,gracias
_________________
R.I.P Kurt Cobain
-------------------------
Volver arriba
david_celta



Registrado: 02 Jun 2005
Mensajes: 45

MensajePublicado: 19/09/2007 4:14 pm
Título:

Con clases:

class nodo
{
private:
int dato;
nodo *sgte;

public:
nodo(int d,nodo*s) {dato=d;sgte=s;}
};

void main()
{
nodo *cabeza=NULL;
cabeza=new nodo(5,cabeza);
cabeza=new nodo(4,cabeza);
}

o alternativamente:

class lista
{
private:
nodo *cabeza;
public:
lista(){cabeza=NULL;}
void insertar(int d){cabeza=new nodo(d,cabeza);}
};

void main()
{
lista l;
l.inserta(5);
l.inserta(4);
}


-----------------------------

Nodo ** pasas un puntero a la funcion para que guarde que no se pierda el valor que asignas

Nodo * pasas el valor del puntero
Nodo &* pasas una referencia al puntero

Por ejemplo si fuera una variable en vez de un puntero (supongo que sabes sobre pasos por valor y pasos por referencia en las funciones)

funcion(int a)
{
a=5;
}

main()
{
a=4;
funcion(a);
//aqui a sigue valiendo 4 porque lo que se pasa a la funcion es el valor de a (que es 4) y no la variable a
}

en cambio si fuera
funcion(&a)
{
a=5;
}

o en todo caso

funcion (*a)
{
*a=5;
}

el valor de a si cambiaria en el main

Sucede lo mismo con los punteros *p,**p y &*p

Solo imagina el *p como una variable cualquiera

De paso puedes cambiarla con el typedef de esta forma:

typedef nodo * tiponodo;

A ver si asi se entiende mejor

typedef int Item;
typedef struct nodo
{
Item dato;
struct nodo* siguiente;
}Nodo;

typedef nodo* tiponodo;


void insertar ( tiponodo * cabeza, Item entrada)
{
tiponodo nuevo;
nuevo=(tiponodo)malloc(sizeof(Nodo));
nuevo->dato=entrada;
nuevo->siguiente=*cabeza;

*cabeza=nuevo;
}


void insertar ( tiponodo & cabeza, Item entrada)
{
tiponodo nuevo;
nuevo=(tiponodo)malloc(sizeof(Nodo));
nuevo->dato=entrada;
nuevo->siguiente=cabeza;

cabeza=nuevo;
}


De todas maneras la version con clases la tienes como una alternativa en c++

- esto es solo una idea, hay que verificarla -
_________________
Este es un bloque de texto que se puede agregar a los mensajes que publique. Texto Compilado en C++. 0 errors y 1 un warning.
Volver arriba
tonilope



Registrado: 16 Oct 2005
Mensajes: 249

MensajePublicado: 19/09/2007 4:32 pm
Título:

Cita:

mi duda es porque se pone (como argumento) Nodo**cabeza y no
Nodo*cabeza ??
se supone que nodo cabeza apunta al primer elemento de la lista y (aunque no lo he puesto)

cabeza->dato = 5;
cabeza->dato=NULL;

luego se hace que el nodo nuevo apunte a cabeza para insertar un nuevo elemento al principio de la lista no ?

de todas formas , podriais ponerme el codigo de :
una lista con un solo elemento int , luego añadir un nuevo elemento a la cabeza ,,,gracias
_________________


La razón de mandarle un puntero doble a la función es porque en C TODOS los parámetros se pasan por copia y si quieres modificar una variable declarada fuera de la función tienes que enviarle un puntero a dicha variable. En tu caso, lo que quieres modificar es el valor de un puntero a NODO, así que le envías un puntero a un puntero a NODO (puntero doble a NODO).


EJEMPLO INCORRECTO:
Código:


typedef int Item;

typedef struct nodo
{
    Item dato;
    struct nodo* siguiente;
}Nodo;


void insertar ( Nodo *cabeza, Item entrada)
{
   Nodo* nuevo;
   nuevo=(Nodo*)malloc(sizeof(Nodo));
   nuevo->dato=entrada;
   nuevo->siguiente=cabeza;
   
   cabeza=nuevo; // MAL. NO estamos modificando el puntero cabeza del main(), sino el de una COPIA de dicho puntero. (Al salir de la función esa copia se destruirá).
}

int main()
{
  Nodo *cabeza=NULL;

//Insertamos un nodo en la lista
insertar(cabeza, 1);

/* Si la función insertar estuviera bien definida cabeza apuntaría al nuevo nodo, pero en este caso, el puntero cabeza NO ha sido modificado dentro de la función insertar() y por tanto sigue valiendo NULL */

return 0;

}


EJEMPLO CORRECTO:
Código:


typedef int Item;

typedef struct nodo
{
    Item dato;
    struct nodo* siguiente;
}Nodo;


void insertar ( Nodo **cabeza, Item entrada)
{
   Nodo* nuevo;
   nuevo=(Nodo*)malloc(sizeof(Nodo));
   nuevo->dato=entrada;
   nuevo->siguiente=*cabeza;
   
   *cabeza=nuevo; //Ahora sí que estamos modificando el valor del puntero cabeza del main

}

int main()
{
  Nodo *cabeza=NULL;

//Insertamos un nodo en la lista
insertar(&cabeza, 1);

/* Ahora el puntero cabeza apunta al nuevo NODO. */

return 0;

}


Salu2 Wink
_________________
Be (void *) my friend
Volver arriba
polly



Registrado: 19 Jul 2007
Mensajes: 61

MensajePublicado: 20/09/2007 3:09 am
Título:

gracias a ambos , ahora ya me aclaro el porque de mandarle NODO** ,
_________________
R.I.P Kurt Cobain
-------------------------
Volver arriba
cheroky



Registrado: 22 Sep 2005
Mensajes: 1372
Ubicación: En ecx esperando un call

MensajePublicado: 21/09/2007 3:17 am
Título:

Intentaré arrojar algo de luz desde la óptica del roll que juega el procesador y la pila en el paso de parámetros, en lo que concierne a los punteros y a lo que comúnmente se denomina "paso por referencia".


Supongamos una función común del estilo:

Código:
void foo(int *p)
{
   *p = 456;
}

Un puntero a int al cual en su indirección se le asigna el valor entero decimal 456.
Para que esto se lleve a cabo la variable puntero p, debe almacenar una dirección de memoria, esta dirección es pasada a la pila desde la función llamante:
Código:

/*           */
int v = 0;
foo(&v);
/* en este punto (v) vale 456 */
/*          */

Esto implica unas serie de instrucciones a bajo nivel, las que se van a reseñar son las típicas en arquitecturas x86 y pueden diferir en el tipo compilador siendo el mecanismo similar.

En la funcion que llama:
Código:
; ..
sub esp, 8    
;Espacio en la pila, (2 * 4 bytes) 1 para la variable (v), 2 para su copia
;El puntero de pila decrece al aumentar esta.

mov [esp+4], 0
;En este punto [esp+4] es nuestra variable

lea eax, [esp+4]
;Copiamos en el registro eax la dirección efectiva de [esp+4]

mov [esp], eax   
;La movemos a la cabeza de la pila TOS (Top of Stack)  convención de llamada C

call foo               
;Llamamos a la función
; ..

En este punto la copia se hace antes de llamar a la función y se observa que esta copia es el valor correspondiente a una dirección de memoria (léase dirección de la variable), lo que se traduce en la rutina C en:
Código:
foo(&v)


Ahora entramos en otro contexto y otro marco de pila (stack frame), el de la función llamada.
Código:
; ..
mov eax, [ebp+8]   
;En este contexto [ebp+8] es el TOS en el stack frame anterior
;movemos a eax la dirección que teníamos

mov [eax], 456         
;esto es similar a una indireción C, movemos 456 a eax
; ..


En este punto ya hemos asignado a la variable (v) un valor efectivo por medio de su dirección de memoria.

Con los punteros a punteros ocurre exactamente lo mismo, salvo que el valor que hay que asignar no corresponde a un valor arbitrario sino a una dirección de memoria efectiva, en este caso que nos ocupa, seria el retorno de la dirección de funciones como malloc, calloc, etc.

Hay que hacer notar que las referencias de C++ se implementan con el mismo mecanismo, es el compilador el que marca las restricciones en tiempo de diseño.


S2.
_________________
- Explícito es mejor que implícito.
- Simple es mejor que complejo.
- Complejo es mejor que complicado.
- Plano es mejor que anidado.
- Ralo es mejor que denso.
Volver arriba
      Índice del Foro elrincondelc.com -> Principiantes C/C++
Página 1 de 1Todas las horas están en GMT - 8 Horas

 
No puede crear mensajes
No puede responder temas
No puede editar sus mensajes
No puede borrar sus mensajes
No puede votar en encuestas

(c) ElRincondelC.com

Un proyecto de UrlanHeat.com