Ayuda con lista enlazada

Iniciado por agrichi, 24 Diciembre 2017, 10:30 AM

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

agrichi

Hola!. Hice una lista simplemente enlazada y no me funciona en algunas partes.
No logro ver donde están los errores
Cuando ingreso un nodo al final de la lista (líneas 89 a 93), el programa se cuelga.
Cuando ingreso un dato que no existe para verlo (lineas 122 a 125), en lugar de mostrar el mensaje de que el dato no se encontró, también se cuelga.
Idem anterior cuando quiero eliminar un dato e ingreso uno que no existe (lineas 186 a 190)
Aquí está el código
Gracias!!


/ Lista simplemente enlazada que permite ingresar el nombre y la edad de personas
// Va ingresando los datos ordenados de menor a mayor según la edad de la persona
// Ademas el programa permite mostrar los datos de todas las personas o de una en particular,
// eliminar una determinada persona o a todas

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

struct nodo{
        char nombre[40];
        int edad;
struct nodo *siguiente;
           };

void IngresarUno(struct nodo **);
void MostrarUno(struct nodo**);
void MostrarTodos(struct nodo **);
void EliminarUno(struct nodo **);
void EliminarTodos(struct nodo **);

int main()
{
int opcion;
struct nodo *p;
p=NULL;
do{
system("cls");
printf("Ingrese la opcion\n\n1:Ingresar datos de una persona\n\n2:Ver datos de una persona\n\n");
printf("3:Mostrar todas las personas\n\n4:Borrar una persona\n\n5:Eliminar todas las personas\n\n6:Salir del programa\n\n");
scanf("%d",&opcion);
switch(opcion)
{
case 1:IngresarUno(&p);
    break;
case 2:MostrarUno(&p);
    break;
case 3:MostrarTodos(&p);
    break;
case 4:EliminarUno(&p);
        break;
case 5:EliminarTodos(&p);
    break; 
case 6:system("cls");
    exit(0);
}
   }while((opcion>=1)&&(opcion<=6));
system("cls"); 
printf("Opcion incorrecta\n\nEl programa terminara\n\n\n");
return 0;
}

void IngresarUno(struct nodo **principio)
{
system("cls");
struct nodo *anterior,*actual,*nuevo;
nuevo=(struct nodo *)malloc(sizeof(struct nodo));
if(!nuevo)
{
  printf("Error al reservar memoria\n\n");
  printf("Presione una tecla para salir\n\n");
  getch();
  exit(1);
  }
printf("Ingrese el nombre: ");
fflush(stdin);
gets(nuevo->nombre);
printf("\n\nIngrese la edad: ");
scanf("%d",&(nuevo->edad));
if(!(*principio)) // Si no hay ningún nodo en la lista, creo el primero
{
  (*principio)=nuevo;
  nuevo->siguiente=NULL;
}
else
{
  anterior=(*principio);
  actual=(*principio);
  while(((nuevo->edad)>(actual->edad))&&(actual)) //Mientras la edad del nodo nuevo sea mayor que la del nodo actual y mientras
  {                                                //no haya llegado al final de la lista, me voy desplazando por la lista
   anterior=actual;                                //actualizando los punteros para evaluar el siguiente nodo
   actual=(actual->siguiente);                     //Cuando salga del while tendre el lugar donde tengo que ubicar el nuevo nodo, y
  }                                                //puede ser al principio de la lista, al final, o entre dos nodos         
  if(anterior==actual)  //Si el nuevo nodo va al principio de la lista, los punteros son iguales
  {
   (*principio)=nuevo;
   (nuevo->siguiente)=actual;
  }
  else if(!actual) //Si llegue al final de la lista(el puntero actual apunta a NULL), entonces el elemento nuevo sera
  {                //el último elemento de la lista
   (anterior->siguiente)=nuevo;
   (nuevo->siguiente)=NULL;
  }
  else //Si no es ninguno de los casos anteriores, entonces el nuevo elemento va entre otros dos
  {   
   (anterior->siguiente)=nuevo;
   (nuevo->siguiente)=actual;
  }
}
printf("\n\n\nPresione una tecla para salir\n\n\n");
getch();
return;
}

void MostrarUno(struct nodo **principio)
{
struct nodo *actual;
int a;
actual=(*principio);
system("cls");
if(!(*principio))
{
  printf("Lista vacia\n\nPresione una tecla para salir\n\n\n");
  getch();
}
else
{
  printf("Ingrese la edad a buscar: ");
  scanf("%d",&a);
  while((a!=(actual->edad))&&actual) //Mientras no coincida la edad que busco con la del nodo, actualizo el puntero para leer el siguiente
   actual=actual->siguiente;
  if(!actual) //Si llegué al final de la lista, entonces no encontré la edad buscada
  {
   printf("\nNo se encontró la edad buscada\n\nPresione una tecla para salir\n\n\n");
   getch();
  }
  else
  {
   printf("\n\nLos datos son:\n\n\n");
   printf("Nombre: ");
   puts(actual->nombre);
   printf("\nEdad: %d\n\n",actual->edad);
   printf("Presione una tecla para salir\n\n\n");
   getch();
  }
}
return;
}

void MostrarTodos(struct nodo **principio)
{
struct nodo *actual;
actual=(*principio);
system("cls");
if(!(*principio))
{
  printf("Lista vacia\n\nPresione una tecla para salir\n\n\n");
  getch();
}
else
{
  while(actual)
  {
   printf("Nombre: ");
   puts(actual->nombre);
   printf("\nEdad: %d\n\n\n",actual->edad);
   actual=actual->siguiente;
  }
  printf("Presione una tecla para salir\n\n\n");
  getch();
}
return;
}

void EliminarUno(struct nodo **principio)
{
struct nodo *actual,*anterior;
int a;
system("cls");
if(!(*principio))
{
  printf("Lista vacia\n\nPresione una tecla para salir\n\n\n");
  getch();
}
else
{
  actual=(*principio);
  anterior=(*principio);
  printf("Ingrese la edad a eliminar: ");
  scanf("%d",&a);
  while((a!=(actual->edad))&&actual) //Mientras no coincida la edad que busco con la del nodo, actualizo el puntero para leer el siguiente
  {
   anterior=actual;
   actual=actual->siguiente;
  }
  if(!actual) //Si llegué al final de la lista, entonces no encontré la edad buscada
  {
   printf("\nNo se encontró la edad buscada\n\nPresione una tecla para salir\n\n\n");
   getch();
  }
  else if(anterior==actual)  //Si los punteros son iguales, entonces el nodo a eliminar es el primero
  {
   (*principio)=((*principio)->siguiente);
   free(actual);
   actual=NULL;
   printf("\n\nPresione una tecla para salir\n\n\n");
   getch();
  }
  else  // Si no es el primero, entonces es cualquiera de los demás
  {
   (anterior->siguiente)=(actual->siguiente);
   free(actual);
   actual=NULL;
  }
}
return;
}

void EliminarTodos(struct nodo **principio)
{
struct nodo *actual;
system("cls");
if(!(*principio))
{
  printf("Lista vacia\n\nPresione una tecla para salir\n\n\n");
  getch();
}
else
{
  actual=(*principio);
  while(actual)
  {
   (*principio)=((*principio)->siguiente);
   free(actual);
   actual=(*principio);
  }
  printf("La lista fue eliminada\n\n\nPresione una tecla para salir\n\n\n");
  getch();
  return;
}
}


do-while

#1
Lo primero es agradecerte el uso de nombres de variable que describen el uso que les vas a dar. Es una buena costumbre, no la pierdas.

He visto algún problema con la lógica:
Línea 79:

while(((nuevo->edad)>(actual->edad))&&(actual))

En la primera condición de la expresión lógica estás desreferenciando actual, que puede ser NULL. Tienes que cambiar el orden:

while((actual && (nuevo->edad)>(actual->edad)))

Así si actual es NULL la condición falla directamente, la segunda parte de la expresión no se evaluaría y no tendrías problemas por desreferenciar un puntero NULL. Por si alguien no lo sabe o no se acuerda, las condiciones de una expresión lógica se evalúan de izquierda a derecha, si en un y falla una de las condiciones la expresión se evalúa automáticamente a false (en C cero) y el resto de condiciones se ignoran, con o pasa lo contrario, si la condición que está a la izquierda es verdad directamente se evalua a true (en C no cero) y el resto no se evalúa.

En la línea 120 está sucediendo lo mismo, tienes que cambiar el orden de las condiciones de la expresión lógica. Además, ya no porque esté mal, lo tienes bien, pero si por la posibilidad de ahorrar ciclos, sabiendo que las edades están ordenadas de menor a mayor, si en lugar de comprobar si la edad del nodo actual es distinta de la edad solicitada compruebas que sea menor, sabrás que el dato que buscas está en una posición más avanzada de la lista, y si la edad del nodo actual es mayor sabrás que directamente la edad que buscas no está en la lista, así que cambiaría ese distinto por un edad_actual < edad_que_buscas (después tendrás que retocar algo el código para saber si se ha salido del bucle por no haberse encontrado (nodo nulo o edad_actual > edad_que_buscas) o porque se ha encontrado la edad.

En la línea 181 también tienes que cambiar el orden de las condiciones y aplicaría los cambios del párrafo anterior.

Más allá de lo dicho no veo ningún error por el que el programa se tenga que quedar colgado (luego le volveré a echar un ojo por si he pasado algo por alto).

¡Saludos!

Por cierto, échale un ojo a este enlace:
https://foro.elhacker.net/programacion_cc/lo_que_no_hay_que_hacer_en_cc_nivel_basico-t277729.0.html

conio.h es una librería que sólo he visto implementada en compiladores de Borland. Su uso no es nada recomendable (no forma parte del estandar de C).

fflush está diseñada para que funcione sobre flujos de salida. Su uso sobre stdin queda indefinido.

Los datos se leen de la entrada estandar después de haber pulsado intro. Ésto deja un salto de línea al final de la entrada. Si utilizas scanf/fscanf ese salto de línea queda en el buffer de entrada. Para limpiar la entrada y evitar el uso de fflush sobre stdin puedes usar el siguiente código:

scanf(lo que sea)
while(getchar() != '\n'); //leemos datos de la entrada hasta encontrar un salto de linea y asi la dejamos limpia.

Lo anterior deberías de hacerlo siempre que sepas que has acabado de leer los datos de una línea del buffer de entrada para evitar que quede "basura". Por ejemplo:

Introduce un entero:
> El usuario escribe "12 abcd culo\n";
Leemos el entero.
En el buffer de entrada queda "abcd culo\n"
Leemos datos de la entrada hasta encontrar el salto de línea.
La entrada ha quedado lista para poder leer los datos que queramos sin que la basura introducida nos moleste.


Las funciones gets/fgets también leen el salto de línea, así que no hay que limpiar nada después de usarlas (si has leído el enlace anterior sabrás porque no es recomendable el uso de gets y porqué deberías de utilizar fgets sobre stdin, sino lee post ya).

Lo mismo se puede aplicar a la situación en la que esperas a que el usuario pulse intro para continuar:

printf("Pulsar intro para continuar...");
while(getchar() != '\n'); //evitamos el uso de getch, que es una funcion exclusiva de conio.h
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!