Compendio de errores, notas y comentarios

Si detectas algún error en El Curso de C de Gorka Urrutia o tienes alguna sugerencia escribe en este foro.
Mensaje
Autor
Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 15 -- Uniones y Enumeraciones

#16 Mensaje por rir3760 » 18/11/2006 9:16 am

* Recomendacion:

En todos los ejemplos de este capitulo falta indicar el valor de retorno de la funcion "main" lo que resulta en un programa correcto pero cuyo valor de retorno al entorno donde se ejecuta es "no definido". Esto se evita con solo añadir como ultima sentencia de main:

Código: Seleccionar todo

return 0;
--------------------------------------------------------------------
* Recomendacion:

En los dos primeros ejemplos de este capitulo despues de la primera llamada a la funcion printf seria bueno utilizar:

Código: Seleccionar todo

fflush(stdout);
Para asegurarnos que la cadena de texto "Escribe tu nombre: " se haya enviado a la salida estandar (stdout).

--------------------------------------------------------------------
* Nota tecnica:

En C estandar si se almacena un valor en una union utilizando uno de sus campos e inmediatamente se recupera este valor utilizando otro campo el resultado depende de la implementacion o "implementation defined".

Lo arriba mencionado sucede en los dos primeros ejemplos de este capitulo, por ejemplo en el primero:

Código: Seleccionar todo

printf("\nTu nombre es: %s\n", pers.nombre);
printf("Tu inicial es: %c\n", pers.inicial);
En mi opinion lo mejor es evitar esto (el resultado dependiente de la implementacion) e indicar claramente que para evitarlo se debe escribir y leer utilizando el mismo campo.

--------------------------------------------------------------------
* Recomendacion:

En el tercero, cuarto y quinto ejemplo de este capitulo seria bueno seguir la convencion en C de nombrar las constantes (tanto macros como de enumeracion) con todos sus caracteres en mayusculas.

Si se sigue esta recomendacion el cuarto ejemplo de este capitulo se veria (mas o menos) asi:

Código: Seleccionar todo

#include <stdio.h>

enum {
   PRIMERO=1, 
   SEGUNDO, 
   TERCERO, 
   CUARTO, 
   QUINTO 
} posicion;

int main(void)
{
   posicion = SEGUNDO;
   printf("posicion = %i\n", posicion);
   
   return 0;
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 16 -- Asignación dinámica de memoria

#17 Mensaje por rir3760 » 19/11/2006 7:25 am

* Ortografia:

En el parrafo describiendo el nombre de malloc:

"La función malloc (Memory allocate - asignar memoria) reservar una parte de la memoria y devuelve la dirección del comienzo de esa parte."

Deberia ser:

"La función malloc (Memory allocate - asignar memoria) reserva una parte de la memoria y devuelve la dirección del comienzo de esa parte."

--------------------------------------------------------------------
* Recomendacion:

En la seccion "Malloc y free" al describir como debe llamarse a la funcion malloc se indica lo siguiente:

"Esto no es necesario en C, ya que lo hace automáticamente, aunque es aconsejable acostumbrarse a usarlo."

En mi opinion tanto el casting como el comentario deben eliminarse ya que en C es valido almacenar un valor de tipo "void *" en una variable de tipo "puntero a X" donde "X" puede ser cualquier tipo de objeto.

La convencion informal para reservar un bloque con espacio suficiente para N elementos es:

Código: Seleccionar todo

p = malloc(N * sizeof *p);
Por ejemplo si se desea reservar un bloque para almacenar 1024 elementos de tipo "int":

Código: Seleccionar todo

int *p;

/* ... */

p = malloc(1024 * sizeof *p); /* 1024 *elementos* */
La ventaja de esta aproximacion es que si despues se cambia el tipo del elemento (por ejemplo de "int" a "long") no es necesario cambiar las llamadas a malloc que sigan esta convencion.

--------------------------------------------------------------------
* Error:

El unico ejemplo de este capitulo tiene varios errores: falta la inclusion del header <stdlib.h>, no se indica el valor de retorno de la funcion "main", se toma por garantizado que la cadena de texto en la primera llamada a printf se envia directamente a la salida estandar y por ultimo cuando se imprime un puntero mediante "%p" este debe convertirse explicitamente al tipo "void *".

El programa corregido es:

Código: Seleccionar todo

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

int main(void)
{
   int bytes;
   char *texto;
   
   printf("Cuantos bytes quieres reservar: ");
   fflush(stdout);
   if (scanf("%d", &bytes) != 1 || bytes < 1)
      return 0;
   
   texto = malloc(bytes);
   
   /* Comprobamos si ha tenido éxito la operación */
   if (texto) {
      printf("Memoria reservada: %d bytes = %d kbytes = %d Mbytes\n", 
         bytes, bytes / 1024, bytes / (1024*1024));
      printf("El bloque comienza en la direccion: %p\n", (void *) texto);
      /* Ahora liberamos la memoria */
      free(texto);
   } else
      printf("No se ha podido reservar memoria\n");
   
   return 0;
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 18 -- Redireccionamiento

#18 Mensaje por rir3760 » 21/11/2006 5:00 pm

* Recomendacion:

Habria que enfatizar que el redireccionamiento descrito en este capitulo aplica o puede utilizarse al ejecutar:

A) Nuestro programa desde el interprete de comandos o shell.
B) La funcion system.


Debido a esto me parece que este capitulo es el indicado para describir en detalle a la funcion system. Por ejemplo:

La funcion system recibe como unico argumento una cadena de texto que es pasada al (si esta disponible) interprete de comandos. Su valor de retorno depende del valor de este argumento:

A) Si es NULL el valor de retorno de la funcion es distinto de cero si esta disponible un interprete de comandos y cero en caso contrario.

B) Si no es NULL el valor de retorno de la funcion depende de la implementacion.

Tambien habra que aclarar que los comandos internos/externos que puedan pasarse a este interprete tambien dependen de la implementacion.

En buen cristiano: la linea de comandos que se pase sera valida (o no) y ejecutara "algo" (o no) dependiendo del interprete de comandos disponible: command.com, cmd.exe, etc.

--------------------------------------------------------------------
* Ortografia:

En la primera seccion "¿Qué es la redirección?" el cuarto parrafo:

"La salida del programa se dirige hacia la salida estándar (stdout, standard output). Normalmente, si no se especifica nada, la salida standard es la pantalla."

Deberia ser:

"La salida del programa se dirige hacia la salida estándar (stdout, standard output). Normalmente, si no se especifica nada, la salida estándar es la pantalla."

--------------------------------------------------------------------
* Recomendacion:

En el primer ejemplo falta indicar el valor de retorno de la funcion main e incluir el header <string.h>.

Un error logico en el programa se generaria si la primera cadena que se introduce es "salir", en este caso se deberia teclear esta misma cadena dos veces para terminar el bucle. Una variacion del programa ligeramente mejor donde se evita esto es:

Código: Seleccionar todo

#include <stdio.h>
#include <string.h>

int main(void)
{
   char texto[100];
   
   while (1){
      gets(texto);
      
      if (strcmp(texto, "salir") == 0){
         fprintf(stderr, "El usuario ha tecleado \'salir\'\n");
         break;
      }
      
      printf( "%s\n",texto );
   }
   
   return 0;
}

Solo faltaria eliminar el uso de gets en favor de fgets.

--------------------------------------------------------------------
* Nota:

En el segundo ejemplo de este capitulo se ejemplifica como reabrir la salida estandar.

Un detalle muy importante a tener en cuenta es que no hay forma en C estandar para "restaurar" los streams estandares (stdin, stdout y stderr) una vez que estos han sido redirigidos con freopen.

--------------------------------------------------------------------
* Error:

El segundo programa de ejemplo de este capitulo tiene errores similares al primero: falta indicar el valor de retorno de la funcion main, incluir el header <string.h>, no detecta si la primera cadena que se introduce es "salir".

Una variante del programa corregido es:

Código: Seleccionar todo

#include <stdio.h>
#include <string.h>

int main(void)
{
   char texto[100];
   
   if (freopen("resultado.txt", "wt", stdout) == NULL)
      return 0; /* salida en caso de error */
   
   while (1){
      gets(texto);
      
      if (strcmp(texto, "salir") == 0){
         fprintf(stderr, "El usuario ha tecleado \'salir\'\n");
         break;
      }
      
      printf( "%s\n",texto );
   }
   
   return 0;
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 19 -- Lectura de Ficheros

#19 Mensaje por rir3760 » 23/11/2006 9:39 am

* Nota:

En los dos ejemplos de este capitulo falta indicar el valor de retorno de la funcion main.

Tambien en ambos falta incluir el header <stdlib.h>:

Código: Seleccionar todo

#include <stdlib.h>
Ya que en este se encuentra el prototipo de la funcion exit.

--------------------------------------------------------------------
* Error:

En el primer ejemplo de este capitulo seria mejor declarar a la variable "letra" de tipo int ya que este es el valor de retorno de la funcion getc.

Tambien seria mejor (por brevedad) no utilizar la funcion feof y trabajar directamente con el valor de retorno de getc (EOF en caso de error o fin de archivo) y evitar el uso de la funcion exit en favor de un simple return:

Código: Seleccionar todo

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

#define NOMBRE_ARCHIVO "origen.txt"

int main(void)
{
   FILE *fichero;
   int letra;
   
   if ((fichero = fopen(NOMBRE_ARCHIVO, "r")) == NULL){
      perror(NOMBRE_ARCHIVO);
      return EXIT_FAILURE;
   }
   
   printf("Contenido del fichero:\n");
   while ((letra = getc(fichero)) != EOF)
      printf("%c", letra);
   
   if (fclose(fichero) != 0){
      perror(NOMBRE_ARCHIVO);
      return EXIT_FAILURE;
   }
   
   return EXIT_SUCCESS;
}

--------------------------------------------------------------------
* Ortografia:

En la seccion "Lectura de un fichero" el parrafo iniciando con:

"Todas las funciones de estrada/salida estándar ..."

Deberia ser:

"Todas las funciones de entrada/salida estándar ..."

--------------------------------------------------------------------
* Nota:

Seria bueno indicar que la forma exacta de la estructura FILE varia dependiendo del compilador que se utilize.

--------------------------------------------------------------------
* Ortografia:

La parte final de la oracion que describe al modo "a":

"... se puedan añadir datos si borrar los existentes."

Deberia ser:

"... se puedan añadir datos sin borrar los existentes."

--------------------------------------------------------------------
* Recomendacion:

En la seccion "Comprobar si está abierto" seria bueno introducir al lector al uso de la funcion perror ya que esta permite que sea el entorno el que genere el mensaje de error. Por ejemplo:

Código: Seleccionar todo

if ((fp = fopen("foo.txt", "r")) == NULL){
   perror("out.txt"); /* el entorno genera el mensaje apropiado */
   return EXIT_FAILURE;
}
Debido a que los valores de retorno que indiquen exito o error varian dependiendo de nuestras herramientas de trabajo seria mejor limitarnos al uso de las macros definidas en <stdlib.h>:

Código: Seleccionar todo

EXIT_SUCCESS
EXIT_FAILURE
para indicar salida con exito o por error, respectivamente.

--------------------------------------------------------------------
* Recomendacion:

La subseccion "Comprobar fin de fichero - feof" termina con el siguiente parrafo:

"Cuando trabajamos con ficheros de texto no hay ningún problema, pero si estamos manejando un fichero binario podemos encontrarnos EOF antes del fin de fichero. Por eso es mejor usar feof."

En mi opinion no es buena idea darle preferencia a la funcion feof sobre el valor de retorno de la funcion, la razon principal es que esta funcion devuelve verdadero solo despues de que una operacion de lectura falle.

En otras palabras un bucle como este:

Código: Seleccionar todo

while (!feof(entrada)){
   fgets(linea, sizeof linea, entrada);
   printf("%s", linea);
}
Imprimiria dos veces la ultima linea. Para evitar esto se debe realizar una operacion de lectura inicial fuera del bucle e intercambiar las operaciones de lectura y escritura dentro del bucle lo que a final de cuentas le da un aspecto "enredado":

Código: Seleccionar todo

fgets(linea, sizeof linea, entrada);
while (!feof(entrada)){
   printf("%s", linea);
   fgets(linea, sizeof linea, entrada);
}
En lugar de esto es mas sencillo y directo utilizar el valor de retorno de la funcion:

Código: Seleccionar todo

while (fgets(linea, sizeof linea, entrada) != NULL)
   printf("%s", linea);

if (ferror(entrada)){
   
   /* manejo de error */
   
}

/* continuacion normal del programa */
--------------------------------------------------------------------
* Recomendacion:

En el segundo ejemplo de este capitulo seria mejor utilizar directamente el valor de retorno de la funcion fgets en lugar de la funcion feof.

Por ejemplo:

Código: Seleccionar todo

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

#define LONG_MAX_LINEA 4096
#define NOMBRE_ARCHIVO "origen.txt"

int main(void)
{
   FILE *fichero;
   char linea[LONG_MAX_LINEA];
   
   if ((fichero = fopen(NOMBRE_ARCHIVO, "r")) == NULL){
      perror(NOMBRE_ARCHIVO);
      return EXIT_FAILURE;
   }
   
   printf("Contenido del fichero:\n");
   while (fgets(linea, LONG_MAX_LINEA, fichero) != NULL)
      printf("%s", linea);
   if (ferror(fichero))
      perror(NOMBRE_ARCHIVO);
   
   if (fclose(fichero) != 0)
      perror(NOMBRE_ARCHIVO);
   
   return EXIT_SUCCESS;
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 20 -- Escritura de Ficheros

#20 Mensaje por rir3760 » 24/11/2006 8:15 am

* Nota:

Tanto en el unico ejemplo como en el primer ejercicio al final del capitulo falta indicar el valor de retorno de la funcion main e incluir el header necesario para utilizar la funcion exit (stdlib.h).

Tambien se declara la variable que debe contener el valor de retorno de la funcion getc como tipo char cuando deberia ser de tipo int.

--------------------------------------------------------------------
* Recomendacion:

El unico ejemplo se puede mejorar bastante si se utiliza la funcion perror para imprimir mensajes de error especificos. Otra mejora seria verificar directamente el valor de retorno de la funcion getc en lugar de utilizar la funcion feof.

Por ejemplo:

Código: Seleccionar todo

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

#define ARCHIVO_ENTRADA "origen.txt"
#define ARCHIVO_SALIDA  "destino.txt"

int main(void)
{
   FILE *origen, *destino;
   int letra;
   
   if ((origen = fopen(ARCHIVO_ENTRADA, "r")) == NULL){
      perror(ARCHIVO_ENTRADA);
      return EXIT_FAILURE;
   }
   
   if ((destino = fopen(ARCHIVO_SALIDA, "w")) == NULL){
      perror(ARCHIVO_SALIDA);
      fclose(origen);
      return EXIT_FAILURE;
   }
   
   while ((letra = getc(origen)) != EOF){
      putc(letra, destino);
      printf("%c", letra);
   }
   
   if (fclose(origen) != 0)
      perror(ARCHIVO_ENTRADA);
   
   if (fclose(destino) != 0)
      perror(ARCHIVO_SALIDA);
   
   return EXIT_SUCCESS;
}

--------------------------------------------------------------------
* Ortografia:

En la descripcion del modo "a":

"... de forma que se puedan añadir datos si borrar los existentes."

Deberia ser:

"... de forma que se puedan añadir datos sin borrar los existentes."

--------------------------------------------------------------------
* Recomendacion:

En la solucion al ejercicio numero uno falta incluir el header necesario para utilizar la funcion strchr (string.h).

Tambien este programa se podria (en mi opinion) mejorar un poco en forma similar al primer programa de este capitulo:

Código: Seleccionar todo

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

#define ARCHIVO_ENTRADA "origen.txt"
#define ARCHIVO_SALIDA  "destino.txt"

int main(void)
{
   FILE *origen, *destino;
   int letra;
   
   if ((origen = fopen(ARCHIVO_ENTRADA, "r")) == NULL){
      perror(ARCHIVO_ENTRADA);
      return EXIT_FAILURE;
   }
   
   if ((destino = fopen(ARCHIVO_SALIDA, "w")) == NULL){
      perror(ARCHIVO_SALIDA);
      fclose(origen);
      return EXIT_FAILURE;
   }
   
   while ((letra = getc(origen)) != EOF)
      if (!strchr("AEIOUaeiou", letra))
         putc(letra, destino);
   
   if (fclose(origen) != 0)
      perror(ARCHIVO_ENTRADA);
   
   if (fclose(destino) != 0)
      perror(ARCHIVO_SALIDA);
   
   return EXIT_SUCCESS;
}

--------------------------------------------------------------------
* Recomendacion:

Debido a que las vocales acentuadas no forman parte del juego de caracteres ASCII seria mejor eliminar este ejercicio ya que se corre el riesgo de que estos caracteres no se impriman correctamente.
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 21 -- Otras funciones para el manejo de ficheros

#21 Mensaje por rir3760 » 25/11/2006 2:34 pm

* Nota:

En los tres ejemplos de este capitulo falta indicar el valor de retorno de la funcion main.

--------------------------------------------------------------------
* Recomendacion:

En la seccion "fread y fwrite" se menciona en el segundo parrafo:

"Como las funciones vistas hasta ahora sólo pueden operar con cadenas deberíamos convertir los valores a cadenas (con la función itoa). Para recuperar luego estos valores deberíamos leerlos como cadenas y pasarlos a enteros (atoi)."

Tomando en consideracion que la funcion itoa no es parte de la biblioteca estandar de C seria mejor mencionar a sprintf en lugar de itoa.

--------------------------------------------------------------------
* Nota:

En la primera seccion no se indica cual es el valor de retorno de la funcion fwrite. Este es el numero de elementos que se escribieron correctamente en el archivo y (si no ocurre algun error) debe ser igual a "numero" entre "tamano".

--------------------------------------------------------------------
* Nota:

En el primer ejemplo de este capitulo falta incluir la directiva:

Código: Seleccionar todo

#include <string.h>
Para tener acceso al prototipo de la funcion strcmp.

--------------------------------------------------------------------
* Ortografia:

En la seccion "fread y fwrite" en uno de sus ultimos parrafos:

"Una vez abierto abrimos estramos en un bucle do-while mediante ..."

Deberia ser:

"Una vez abierto entramos en un bucle do-while mediante ..."

--------------------------------------------------------------------
* Recomendacion:

El segundo programa de ejemplo se podria mejorar bastante si se incluyen todas las comprobaciones de errores y se utiliza directamente el valor de retorno de la funcion fread en lugar de la funcion feof.

Por ejemplo:

Código: Seleccionar todo

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

struct {
   char nombre[20];
   char apellido[20];
   char telefono[15];
} registro;

#define NOMBRE_ARCHIVO "nombres.txt"

int main(void)
{
   FILE *fichero;
   
   if ((fichero = fopen(NOMBRE_ARCHIVO, "r")) == NULL){
      perror(NOMBRE_ARCHIVO);
      return EXIT_FAILURE;
   }
   
   while (fread(&registro, sizeof(registro), 1, fichero) == 1){
      printf("Nombre: %s\n", registro.nombre);
      printf("Apellido: %s\n", registro.apellido);
      printf("Telefono: %s\n\n", registro.telefono);
   }
   if (ferror(fichero)){
      perror(NOMBRE_ARCHIVO);
      fclose(fichero);
      return EXIT_FAILURE;
   }
   
   fclose(fichero);
   return EXIT_SUCCESS;
}

--------------------------------------------------------------------
* Error:

El tercer ejemplo de este capitulo tiene dos errores: uso de vocales acentuadas y uso de "%D" cuando los indicadores de tipo para el tipo long int son "%ld" y "%li".

Una version de este ejemplo sin los errores mencionados es:

Código: Seleccionar todo

#include <stdio.h>

int main(void)
{
   FILE *fichero;
   long posicion;
   
   fichero = fopen("origen.txt", "r");
   
   printf("Que posicion quieres leer? ");
   fflush(stdout);
   scanf("%ld", &posicion);
   
   if (fseek(fichero, posicion, SEEK_SET) == 0)
      printf("En la posicion %ld esta la letra %c.\n", posicion, getc(fichero));
   else
      printf("Problemas posicionando el cursor.\n");
   
   fclose(fichero);
   return 0;
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
rir3760
Mensajes: 7553
Registrado: 01/10/2004 11:00 pm
Ubicación: Mexico

Capitulo 22 -- Listas enlazadas simples (I)

#22 Mensaje por rir3760 » 25/11/2006 2:37 pm

*Error:

En el unico programa de ejemplo se llama en "main" a la funcion "mostrar_lista" pasandole un argumento:

Código: Seleccionar todo

case '3':
   mostrar_lista(primero);
   break;
Cuando esta funcion esta declarada (y definida) para no recibir argumentos.

--------------------------------------------------------------------
* Recomendacion:

El programa de ejemplo tiene algunos "detalles" que pueden mejorarse y al mismo tiempo evitar posibles mensajes de advertencia en algunos compiladores:

* Para tener acceso a los prototipos de las funciones malloc, exit y getch se deben incluir los headers:

Código: Seleccionar todo

#include <stdlib.h>
#include <conio.h>
* Falta indicar el tipo de retorno de la funcion main.

* Agregar los prototipos de las funciones permitiria desarrollar el programa sin importar el orden de las funciones.

* En C la conversion explicita o casting al llamar a malloc no es necesaria. Por lo mismo esta llamada en la funcion "anadir_elemento":

Código: Seleccionar todo

/* reservamos memoria para el nuevo elemento */
nuevo = (struct _agenda *) malloc (sizeof(struct _agenda));
if (nuevo==NULL) printf( "No hay memoria disponible!\n");
Se puede abreviar a (incluyendo la salida del programa en caso de error):

Código: Seleccionar todo

/* reservamos memoria para el nuevo elemento */
if ((nuevo = malloc(sizeof *nuevo)) == NULL){
   printf("No hay memoria disponible!\n");
   exit(EXIT_FAILURE);
}
* Tambien seria recomendable eliminar el uso de vocales acentuadas en el programa.

El programa con las modificaciones es:

Código: Seleccionar todo

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

struct _agenda
{
   char nombre[20];
   char telefono[12];
   struct _agenda *siguiente;
};

struct _agenda *primero, *ultimo;

void mostrar_menu(void);
void anadir_elemento(void);
void mostrar_lista(void);

int main(void)
{
   char opcion;
   
   primero = (struct _agenda *) NULL;
   ultimo = (struct _agenda *) NULL;
   do {
      mostrar_menu();
      opcion = getch();
      switch (opcion) {
      case '1':
         anadir_elemento();
         break;
      case '2':
         printf("No disponible todavia!\n");
         break;
      case '3':
         mostrar_lista();
         break;
      case '4':
         exit(1);
      default:
         printf("Opcion no valida\n");
         break;
      }
   } while (opcion != '4');
   
   return EXIT_SUCCESS;
}

void mostrar_menu(void)
{
   printf("\n\nMenu:\n=====\n\n");
   printf("1.- Agregar elementos\n");
   printf("2.- Borrar elementos\n");
   printf("3.- Mostrar lista\n");
   printf("4.- Salir\n\n");
   printf("Escoge una opcion: ");
   fflush(stdout);
}

/* Con esta función añadimos un elemento al final de la lista */
void anadir_elemento(void)
{
   struct _agenda *nuevo;
   
   /* reservamos memoria para el nuevo elemento */
   if ((nuevo = malloc(sizeof *nuevo)) == NULL){
      printf("No hay memoria disponible!\n");
      exit(EXIT_FAILURE);
   }
   
   printf("\nNuevo elemento:\n");
   printf("Nombre: ");
   fflush(stdout);
   gets(nuevo->nombre);
   
   printf("Teléfono: ");
   fflush(stdout);
   gets(nuevo->telefono);
   
   /* el campo siguiente va a ser NULL por ser el último elemento
      de la lista */
   nuevo->siguiente = NULL;
   
   /* ahora metemos el nuevo elemento en la lista. lo situamos
      al final de la lista */
   /* comprobamos si la lista está vacía. si primero==NULL es que no
      hay ningún elemento en la lista. también vale ultimo==NULL */
   if (primero == NULL){
      printf("Primer elemento\n");
      primero = nuevo;
      ultimo = nuevo;
   }else {
      /* el que hasta ahora era el último tiene que apuntar al nuevo */
      ultimo->siguiente = nuevo;
      /* hacemos que el nuevo sea ahora el último */
      ultimo = nuevo;
   }
}

void mostrar_lista(void)
{
   struct _agenda *auxiliar; /* lo usamos para recorrer la lista */
   int i;
   
   i = 0;
   auxiliar = primero;
   printf("\nMostrando la lista completa:\n");
   while (auxiliar != NULL) {
      printf("Nombre: %s, Telefono: %s\n",
              auxiliar->nombre, auxiliar->telefono);
      auxiliar = auxiliar->siguiente;
      i++;
   }
   if (i == 0)
      printf("\nLa lista esta vacia!!\n");
}

C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

Avatar de Usuario
david_celta
Mensajes: 165
Registrado: 02/06/2005 11:00 pm

#23 Mensaje por david_celta » 19/11/2007 6:15 pm

Del curso de C, la última parte, la de Estructuras de Datos Dinámicos - Listas Enlazadas Simples - (la única que he mirado por cierto)

Siempre debemos tener un puntero del tipo _agenda para recordar la posición en memoria del primer elemento de la lista. Si perdemos este puntero perderemos la lista completa, así que mucho cuidado. Este puntero se definiría así:

struct _agenda primero;
Falta el * antes del nombre de la variable primero.

Parece que a rir3760 también se le pasó. Siempre se empieza por hacer una buena declaración para no cometer errores que después no sabemos ni por qué esta fallando el programa.

(lamento no seguir la recomendación de no publicar aquí pero bueno ya lo hice)
Falls of Rauros

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados