Home
Username:
Password:
C Programming (Español) Tutorials

Biblioteca de Funciones Estándar del C / Soluciones del Capítulo 16




Visitors to VTC.com will be able to view all introductory videos for each training course.
Free Trial Members will gain access to first three chapters for each training course.
Full Access Members have full access to VTC.com’s entire library of video tutorials.


Learn More

Subtitles of the Movie

Estas son las soluciones a los ejercicios del capitulo 16. Este es el archivo "dice.c" que simula el lanzamiento de un dado. Veamos el codigo. Aqui solicitamos el numero de rodamientos y distribuimos el generador de numeros aleatorios. Mas adelante explicare esta directiva "NOT_USING_MACRO"É que basicamente contiene este codigo: tenemos un ciclo "for", la cantidad de rodamientos deseados y aquiÉ ejecutamos el generador de numeros aleatorios. Aqui realizamos la operacion de modulo con la directiva "SIDES" de esta manera: "number = number", modulo y "SIDES". Si "SIDES" es 6, obtendremos un numero entre el rango de 0 a 5. Recuerde que cualquier numero con la operacion modulo 6 debe ser un numero entre el rango de 0 a 5. Este es un truco ingenioso. ÀUsted penso que debia dividirlo por cualquier numero? Esa division no es apropiada. Sumamos 1 para obtener un numero entre el rango de 1 a 6É y para hacerlo he escrito un "macro" como el que vimos en el capitulo del pre-procesador. Aqui escribimos un "macro" que parece una funcion, al igual que todos los "macros". Especificamos el numero y el rango deseados para que este sea tratado como un solo numero. Si aqui especificamos el valor 6, obtendremos un rango de 1 a 6. Podriamos tener el valor 28.216 o 6 y lo convertiremos en un numero entre el rango de 1 a 6É mediante la utilizacion de estos parentesis y la operacionÉ modulo 6 y la suma del valor 1. Podemos hacerlo con o sin el "macro". Al utilizar el "macro", este es el proceso. No hay que tener una variable "number" ni hay que ejecutar estos pasosÉ sino que podemos hacer todo dentro de la funcion "printf" de esta manera. Basicamente, este es el numero aleatorio y esta es la operacion modulo 6, asi que existen dos maneras de hacerlo. Para comprobarlo podemos incluir o no la directiva "#define NOT_USING_MACRO"É y asi obtendremos una u otra version del proceso. Esto es todo. Ahora veamos el gran programa en el que se ha convertido la base de datos de CDs. He agregado el archivo "file.c" al proyectoÉ y los demas archivos han sido modificados de alguna manera. Estos cambios son extensosÉ y va a tomar bastante tiempo describirlos. Comenzamos con la funcion "main" y veremos el proyecto desde un punto de vista general. Luego veremos algunas funciones mas pequenas como las del archivo "file.c", etc. Algunos archivos no han cambiado. El archivo "output.c" no ha cambiado y la unica modificacion de "input.c"É fue revertir el uso de arreglos para el titulo y el artista. Aqui hay tan pocos cambios que no es necesario revisarlos, pero la funcion "main" tiene muchas modificaciones. Tambien tenemos el nuevo archivo "file.c". Veamos la funcion "main". Aqui la tenemos. Vamos al final del programa. Tenemos las funcion "atexit"É con el parametro "goodbye". Esta es la funcion "goodbye"É con un llamado a la funcion "reset_everything", la cual veremos en un momento. Desplegamos "Press enter to exit the program" y observe que esta funcion es de tipo "static void". La funcion "main" toma los dos parametrosÉ "argc" y "argv". Recuerde que "argc" es el nombre del programa, asi que este debe ser el mayor. Luego procesamosÉ la cadena del primer parametro en la linea de comandos, la cual de hecho es la segunda cadena del arreglo de cadenas. La primera cadena es el nombre del programa. Aqui llamamos a la funcion "general_open". que veremos en un momento, pero tal vez usted imagineÉ que esta es una funcion que abre un archivo y lee algunos CDs en el. Desplegamos una vez "Welcome to the CD database" y entramos a un ciclo infinito para desplegar el menu. Leeremos una opcion ingresada por el usuarioÉ y el menu se desplegara de nuevo, repitiendose este procesoÉ una y otra vez hasta que se ingrese la opcion 9 para salir. Veamos la funcion "display_menu". Indicamos cuantos registros existen actualmente, y si esta es la primera vez, este valor sera 0. Tenemos una variable global "fname" para el nombre del archivo. Veamos. Aqui tenemos cuatro variables globales, y aunque dije que estas no me agradan, las variables estaticas globales no son tan malas porque su uso esta restringido a este archivo, asi que debemos tener el nombre del archivo que estamos procesando, ya sea que lo hayamos abierto o que lo hayamos guardado. Tambien tenemosÉ el arreglo "cds" asignado dinamicamente. Este esta inicializado con el valor "null"É porque todas las variables estaticas se inicializan con el valor 0 y "null" es otra forma del valor 0. Tambien tenemos la variable "count" inicializada con el valor 0 y una variable booleana. llamada "changes"É utilizada para determinar por ejemplo el despliegue de la confirmacionÉ de la eliminacion de los cambios. Obviamente, debemos saber si se han realizado cambios, asi queÉ tendremos 1 si se han realizado y 0 si no se han realizado. Regresemos a la funcion "display_menu". Si la longitud del nombre del archivo es 0, es decir, si este no se ha ingresado, indicamos que el archivo no existe. De lo contrario, lo desplegamosÉ junto al menu de nuevo. Esta es la apariencia del menu. Aqui utilizamos de nuevo la funcion "read_int". que es util en ocasiones y que restituye un enteroÉ que corresponde a un valor de 0 a 9. Tenemos una sentencia "switch"É dentro de un ciclo porque el usuario podria haber ingresado un numero no valido. Es decir, si no se ha ingresado un valor de 0 a 9 desplegamos "Invalid selection, please try again"É y solicitamos otro numero ejecutando de nuevo el ciclo. Si se ha ingresado otro valor ejecutaremos las funcionesÉ "file_new", "file_open", "file_save", "file_save_as", "file_append", "edit_add"É que crea un nuevo registro, "view_display_one", "view_display_all" o "quit". Esto es un muy sencillo. Llamaremos a la funcion que corresponda a la opcion. Solicitamos la pulsacion de "Enter" para continuar despues de completar cada funcionÉ y esa es la funcionÉ "display_menu". Veamos cada una de estas funciones. En la funcion "quit" llamamos a la funcion "are_you_sure"É que he escrito y que restituye un valor 1 o 0É dependiendo si el usuario responde si o no. Si la respuesta es si obtenemos 1É y llamamos a la funcion "exit" con el valor 0. En la funcion "are_you_sure", si no hay cambios, no es necesario preguntar al usuarioÉ si esta seguro de continuar. Recuerde que si hemos guardado los cambios en el Notepad, al salir de el no se nos pregunta si estamos segurosÉ sino que solo salimos. Pero si hemos realizado cambios y no los hemos guardado, se nos pregunta si estamos seguros de continuar. De manera similarÉ aqui tenemos esta pregunta. Aqui llamamos de nuevo a la util funcion "yesno" para responder dicha preguntaÉ y esto es todo lo que hace la funcion "are_you_sure". Regresamos a "quit". y esto es todo. Obviamente, al ejecutar la funcion "exit" se procesa la funcion "atexit". Veamos el resto de funciones. Busquemos la funcion "file_new". que debe estar en la parte superior. Nos desplazamos y aqui la tenemos. Aqui de nuevo llamamos a la funcion "are_you_sure" para asegurarnos de que el usuario desea crear un nuevo archivo. Si escribimos algunas palabras en el Notepad y vamos al menu "File>New"É este nos pregunta si estamos seguros de eliminar los cambios. En tal caso llamamos a la funcionÉ "reset_everything" para eliminar los cambios, pues si tenemos un gran arreglo de CDs asignado dinamicamente, debemos liberar esa memoria. Veamos dicha funcion. Aqui tenemos la funcion "reset_everything"; si tenemos algunos CDs asignados, es decir, si el apuntador al arreglo "cds" no tiene el valor "null", liberamos la memoria y le asignamos el valor "null". Observe que la funcion "free" no asigna el valor "null" al arreglo "cds" sino que debemos hacerlo nosotros mismos. Asignamos el valor 0 a "count" y a "changes" y despejamos el nombre del archivoÉ porque crearemos un nuevo archivo. Recuerde que en la funcion "goodbye" tambien restauramos todos los valoresÉ y especificamente debemos liberar la memoria del arreglo "cds". Podriamos no hacerlo al salir del programa, ya que si no la liberamos el sistema operativo lo hara por nosotros, pero es mejor liberar manualmente toda la memoria asignada. Este es un habito muy conveniente. Al final del programa restauramos todo igualmente. Regresemos a la funcion "file_new"É que es bastante sencilla. Simplemente eliminamos los cambios y restauramos todos los valores iniciales. Continuamos con "file_open" y "file_save". Primero veamos la funcion "file_open". Aqui reservamos un espacio para el nombre del nuevo archivoÉ y observe que esta directiva "#define" indica el tamano del mismo. De nuevo llamamos a la funcion "are_you_sure". para indicar que no se han guardado los cambios. Si se han guardado no se formulara esta pregunta, pero en caso contrario se llamara a la funcion "are_you_sure". Si la respuesta es no, simplemente regresamos porque no hay mas acciones que realizar. De lo contrario, solicitamos al usuario el nombre del nuevo archivo que desea abrir. Observe que el archivo podria no existir, asi que no podemos ubicarlo directamenteÉ en la variable "fname" que almacena el nombre del mismo. Si hubo un error y no pudimos abrir el nuevo archivo especificado, aun querremos conservar el archivo existente, asi que de nuevo llamamos a "general_open"É que utilizamos anteriormente al emplear los parametros de la linea de comandos "argv" y "argc". Revisemos la funcion "general_open". Esta basicamente abre un archivo y lee algunos CDs dentro de el. Asignamos el arreglo "cds" al apuntador "new_cds" y tenemos la variable "new_count" en caso de falla. Leemos los CDs de un archivo a otro. Esta funcion corresponde al archivo "file.c" que veremos mas adelante. Este abre este archivo, y si existen o no CDs dentro de el, los pasara de regreso aquiÉ mientras el apuntador les asigna un espacio dinamicamente. La variable "new_count" contendra la cantidad de CDs en cuestion, asi que debemos pasar su direccion de maneraÉ que la funcion "open_file" pueda modificarla. La funcion "open_file" asignara el valor -1 a la variable "new_count" si ocurre un error al abrir el archivo. En tal punto ejecutamos la funcion "perror", desplegamos "Could not open the file" y regresamos. Si no podemos abrir el archivo, la lista original de CDs no habra cambiadoÉ porque hemos apuntado este conjunto de CDs a una nueva variable llamada "new_cds". El arreglo "cds" ahora representa los CDs de un archivo, asi que debemos hacer que este sea el arreglo actual. Debemos utilizar el arreglo del nuevo archivo, asi que restauramos todos los valoresÉ para eliminar los CDs actuales si estos existen. Ya hemos llamado a la funcion "are_you_sure" en la funcion "file_open", asi que sabremos que el usuario desea continuar. Restauramos todos los valores y configuramos las tres variables globalesÉ para obtener el valor 0 en todos los elementosÉ y no tener que asignarlo manualmente. Luego indicamos que el archivo ha sido leido exitosamente. Esa es la funcion "file_open". Revisaremos la funcion "open_file"É mas adelante al examinar el contenidoÉ del archivo "file.c" en una sola accion. Aqui tenemos la funcion "general_save", asi que veamos su utilizacion. Esta es la funcion "file_save"É y recuerde que esta es la opcion 3. Tambien tenemos la opcion 4 "file_save_as"É y la opcion 5 "file_append". Todas ellas llaman a la funcion "general_save"É y esa es su unica accion. La funcion "general_save" toma como parametrosÉ dos valores booleanos: uno para indicar si queremos adjuntar un archivoÉ y otro para indicar si queremos guardarlo con otro nombre. Estos podran ser 0 o 1É y son mutuamente exclusivos, pues no podemos adjuntar y guardar un archivo con otro nombre al mismo tiempo. Primero tenemos un guardado regularÉ donde no adjuntamos nada y solo guardaremos con otro nombre si el nombre del archivo actual esta vacio. Recuerde que en el Notepad, si guardamos un archivo sin antes especificar su nombre, este sera solicitado. Es decir, estaremos guardandolo con otro nombreÉ si antes no se ha especificado el nombre del archivo. En tal caso no estaremos adjuntando un archivo, y si en efecto estamos adjuntando un archivo, no estaremos guardando con otro nombre. Estamos reutilizando este codigo, pues tenemos una funcion "general_save" que realiza cualquier tipo de guardadoÉ y la estamos llamando de tres maneras diferentes. Aqui estamos reutilizando un codigo, y aunque es un poco complejo porque tenemos ceros, unos, indicadores, etc., la apertura y el cierre de los archivos se realizaÉ en este unico lugar, al igual que la solicitud de los nombres de los nuevos archivos. Si estamos adjuntando o guardado un archivo con otro nombre, debemos solicitar el nuevo nombreÉ y lo asignaremos a esta variable "new_fname". Si no realizamos ninguna de estas dos acciones, estaremos realizando un guardado regularÉ y utilizaremos el archivo actual. Aqui ejecutamos algunos procesos un poco complicadosÉ si intentamos escribir datos en un archivo existente. He escrito esta funcion "file_exists" en el archivo "file.c"É y la veremos mas adelante. Si se ha especificado el nombre del archivoÉ y si dicho nombre de archivo existe, solicitamos al usuario que confirmeÉ si desea sobrescribir ese archivo. Si la respuesta es no, regresamos. Realizamos estas tres acciones consecutivamente. No se si usted sepa este aspecto, pero voy a mencionarlo. Si "save_as" es falsa, no se realizan estas acciones, y si "file_exists" es falsa, no se realizara esta accion. Es muy util aprovechar este aspecto de los operadores ampersand. Al utilizar dos signos ampersand, si la primera condicion es falsaÉ no se verifica la segunda condicionÉ por parte del compilador. Podemos asegurar que ocurrira este comportamiento exacto. Si no se ejecuta la funcion "save_as", estas dos verificaciones no ocurriran, pues si el archivo no existe no querremos preguntar al usuarioÉ si esta seguro de querer sobrescribirlo. En este punto habremos ingresado exitosamente el nombre del archivo si es necesario o lo habremos obtenido desde aqui. Nos aseguramos de poder sobrescribirlo, y si el archivo existe, simplemente lo guardamos. Esta funcion "save_file" se encuentra en el archivo "file.c" que veremos mas adelante. Basta decir que le pasamos el nombre del archivo, el arreglo "cds", la variable "count" con la cantidad de CDsÉ y si queremos o no adjuntar un archivo. Luego indicamos si el proceso fue exitosoÉ y asignamos el valor 0 a la variable "changes", pues ya se han guardado los cambios. Si estamos guardando con otro nombre, debemos asegurarnos de copiar el nuevo nombre en la variable "fname" original. El resto del programa es casi igual que antes. En la funcion "edit_add"É solicitamos los detalles del CD y solo tenemos una accion ligeramente compleja: cada vez que leemos los detalles de un nuevo CD debemos reasignar un espacio para el arreglo "cds", es decir, si tenemos 10 CDs en el arreglo asignado dinamicamenteÉ y decidimos crear un onceavo CD, debemos llamar a la funcion "realloc" y no a "malloc" o a "calloc". ÀLo recuerda? La re-asignacion de memoria se realizaÉ en esta funcion ubicada en el archivo "utils.c", la cual reasigna todo el espacio del arreglo "cds". Veremos esto mas adelante. Aqui incrementamos el tamano del arreglo en 1, copiamos el CD que hemos leidoÉ en la posicion anterior e incrementamos la variable "count" en 1. Se han realizado cambios, asi que debemos preguntar al usuario si esta seguro de continuar antes de salir o realizar otra accion. En la funcion "view_display_one", si no hay nada que mostrar lo indicamos. De lo contrario, solicitamos un numeroÉ utilizando de nuevo la ingeniosa funcion "read_int". Solicitamos el numero del CD que queremos leer y verificamos que este se encuentre entre el rango correctoÉ de 1 al valor de "count". Si no es asi desplegamos un mensaje de error y preguntamos de nuevo. Luego tenemos el despliegue de los detalles de ese CD. Primero nos aseguramos de que estos existan y luego los desplegamos. Esto es todo. Pedimos que se pulse "Enter" entre cada visualizacionÉ como ya hemos vistoÉ y luego tenemos la funcion "quit" que ya revisamos. Este es el archivo "main.c". Veamos ahora las operaciones con los archivos en "file.c". Esta es la funcion "file_exists" con la cual verificamos si existe un archivoÉ y que restituye 1 si este existe o 0 si este no existe. Lo abrimos en modo de lectura y si este proceso falla desplegamos "The file does not exist". De lo contrario cerramos el archivo y restituimos el valor 1. Existe una mejor manera de hacerloÉ mediante una funcion de biblioteca llamada "stat" que usted puede revisar si desea. Aunque no la conozca, recuerde que podria ser utilÉ para abrir el archivo en modo de lectura y cerrarlo de nuevo inmediatamente. Aqui tenemos la funcion "save_file"É que toma el nombre de un archivo, el arreglo "cds", la cantidad de CDs en elÉ y si vamos o no a adjuntar un archivo. Si no lo adjuntamos, solo estaremos escribiendo en el archivo. Esta es una sencilla funcion que intenta abrir un archivo. Aqui utilizamos el operador condicional. Si "append" es verdadera adjuntamos un archivo binario. De lo contrario, escribimos un archivo binario regular. Si "fptr" tiene el valor "null", restituimos -1 para indicar una falla. Tenga en cuenta que la funcion "errno" es ejecutada en este casoÉ si la funcion "fopen" presenta alguna falla. Aqui escribimos todos los registros mediante la funcion "fwrite" al pasarle el arreglo "cds", el tamano de 1 CDÉ ubicado en la primera posicion del arreglo, la cantidad de CDs en elÉ y el apuntador al archivo en cuestion. Todo esto debe ser igual a "count" porque estamos escribiendo esa cantidad de CDs, asi que si no es igual, el valor de restitucion sera 0. De lo contrario, este sera -1. El 0 indica que no hubo errores y el -1 indica que hubo al menos un error, como indicamos aqui: tendremosÉ el valor -1 si hay errores o el valor 0 si el proceso fue exitoso. Finalmente, la apertura del archivo es muy sencilla. Aqui abrimos el archivo en particular y leemos algunos CDs dentro de el. Si hay CDs en el, asignamos un espacio y los leemos uno por uno. Al terminarÉ configuramos este numero que representa la cantidad de los CDs y restituimos un apuntador al area de memoria asignada. Si hay algun error asignamos el valor -1 a "num"É para indicar que algo salio mal y obtener un valor "null". Si falla la apertura del archivo asignamos -1 a "num" y restituimos un valor "null" como esperamos. Si el archivo fue abierto exitosamente, asignamos 0 al contador "num". Haremos esto mientras podamos leer los datos del archivo. Aqui tenemos un espacio reservado para un CD correspondiente a la variable "cd" a la cual asignaremos esos datos. Luego tenemos el tamanoÉ y la cantidad de 1 CD. Haremos esto hasta llegar al final del archivo. Como antes, tenemos la funcion "increase_array" con la funcion "realloc" para incrementar el tamano del arreglo. Luego copiamos la estructura del CD leido a partir del archivo al final del arreglo e incrementamos el tamano en 1. Observe la disposicion de estos parentesis. Intentamos incrementar la variable "num", pero este es un apuntador, asi que debemos tomar su contenido. Si no incluimos los parentesis, se incrementaria el apuntador y se tomaria su contenido, peroÉ Àque hariamos con ese contenido? Nada, y aunque no espero que usted lo sepa, es una norma en el C que el operador "++" tenga una mayor precedencia que el operador asterisco. Este es uno de los aspectos que me desagradan del C, pero podemos corregirlo asi. Cerramos a "fptr", restituimos a "new_cds" y "num" se configura automaticamente porque la incrementamos sobre la marcha. En algun lugar liberaremos esa memoria, y al pensar en la logica del archivo "main.c", veremos que esta sera liberada en el momento apropiado. Veamos un ultimo aspecto en el archivo "utils.c"É correspondiente al incremento del tamano del arreglo "cds". Veamos. Este es el archivo "utils.c" y la funcion "increase_array". Voy a rescribir el comentario. Le pasamos las variables "oldarra" o arreglo anterior, "oldcount" o cantidad anterior e "increase" o incremento. Estas dos pueden estar vacias, es decir, "oldarr" podria tener el valor "null" y "oldcount" el valor 0. Si "oldarr" es "null", asumimos que "oldcount" es 0 y viceversa y este es el requerimientoÉ para utilizar esta funcion. Si pasamos el valor "null" en "oldarr", debemos pasar 0 en "oldcount" y viceversa. Luego configuramos a "newarr" o nuevo arreglo. Si no habia elementos en el arreglo, ejecutamos la funcion "malloc". De lo contrario, ejecutamos la funcion "realloc". Ya vimos como se ejecutan las funciones "malloc" y "realloc". Si el valor restituido es "null", desplegamos "The program is out of memory, exiting"É y restituimos la variable "newarr". Esto es todo. Veamos si funciona; compilamosÉ cada uno de los cinco archivos del proyecto mas el archivo de encabezado, no obtenemos errores y ejecutamos. Veamos lo que ocurre. Aparece "Welcome to the CD database", tenemos 0 registros en el sistema y ningun nombre de archivo actual. Vamos a crear un nuevo registro. Escogemos la opcion 6 e ingresamos el titulo, el artista, 3 pistas, este sera un album, un precio de 6 dolares y pulsamos "Enter". Ahora tenemos un registro y ningun nombre de archivo. Vamos a guardar la lista, asi que escogemos la opcion 3 y a este archivo lo llamamos "cd.dat". Debemos darle algun nombre. Aparece "List saved successfully, 1 records". Vamos a guardar de nuevo. Hemos guardado la lista por segunda vezÉ y ahora vamos a guardarlo con otro nombre. Escogemos la opcion 4É e ingresamos un nombre inventado que no existeÉ como "j:\xyz.dat". Se nos indica que no podemos guardar la lista porque este directorio no existe. Teoricamente aun tendremos el nombre "cd.dat" y no adoptamos ningun nombre erroneo. Veamos el cierre del archivo, asi que escogemos la opcion 9 y pulsamos "Enter" para salir del programa. Ejecutamos de nuevo y escogemos la opcion 2 para leer el archivo que creamos. Ingresamos "cd.dat" y aparece "Successfully read file, 1 new records". Ahora tenemos el nombre "cd.dat" y tenemos 1 registro en el sistema. Ahora agregaremos un nuevo CD, asi que escogemos la opcion 6. Ahora tenemos 2 registros en el sistema. Escogemos la opcion 9 para salir yÉ se nos pregunta si estamos seguros. Antes no fue asi porque guardamos los cambios. No vamos a salir sino que crearemos una nueva lista. Escogemos la opcion 1É y de nuevo se nos pregunta porque no hemos guardado los cambios. Diremos que noÉ y en cambio guardaremos los cambios directamente en el archivo. Se guarda la lista y ahora el archivo contiene 2 registros. Al escoger la opcion 1, se crea una nueva listaÉ sin ningun problema. Ahora tenemos 2 registros en el sistema y ningun nombre de archivo, pero el archivo "cd.dat" contiene los dos CDs guardados. Podriamos ver las otras opciones, pero se que funcionan porque las verifique antes de completar este programa. Usted puede compilarlo y verificarlo si desea y estare gustoso de recibir cualquier informeÉ de la presencia de "bugs". Si encuentra algun "bug", estare complacido de poder felicitarlo.

Tutorial Information

Course: C Programming (Español)
Author: Mark Virtue
SKU: 33759
ISBN: 1-933736-81-X
Release Date: 2007-04-16
Duration: 21.5 hrs / 139 lessons
Captions: Available on CD and Online University
Compatibility: Vista/XP/2000, OS X, Linux
QuickTime 7, Flash 8

VTC Sign up & Benefits

  • Unlimited Access
  • 98,729 Video Tutorials (23,265 free)
  • Video Available as Flash or QuickTime
  • Over 1026 Courses
  • $30 for One Month Access
  • Multi-User Discounts Available