-
Ejercicio 0202r01.-
Construir una estructura de datos adecuada para una base de datos de personal, que contendrá campos para nombre, dos apellidos, dirección, teléfono y DNI (
struct Registro
, por ejemplo). Escribir un programa dotado de funciones que lean y escriba los campos de un registro de este tipo,sin efectuar comprobaciones de longitud.
La solución puede ser la siguiente:
#include<stdio.h>
#define MAX_PERSONAS 10
typedef struct Registro {
char nombre[20];
char ape_1[20];
char ape_2[20];
char direc[40];
char telef[15];
char DNI[15];
} Registro;
typedef Registro * RegistroRef;
Registro bdd[MAX_PERSONAS];
void leer(RegistroRef f);
void escribir(RegistroRef f);
int main(int argc,charRef argv[]) {
Registro una_ficha;
printf("\nPor favor, escriba los datos solicitados:\n\n");
leer(&una_ficha);
printf("\n\nLos datos introducidos eran como sigue:\n\n");
escribir(&una_ficha);
printf("\n\nTerminación normal del programa.\n\n");
return 0;
}
void leer(RegistroRef f){
printf("\nPor favor, escriba su nombre : ");gets(f->nombre);
printf("\nPor favor, escriba su primer apellido : ");gets(f->ape_1);
printf("\nPor favor, escriba su segundo apellido : ");gets(f->ape_2);
printf("\nPor favor, escriba su dirección : ");gets(f->direc);
printf("\nPor favor, escriba su teléfono : ");gets(f->telef);
printf("\nPor favor, escriba su DNI : ");gets(f->DNI);
}
void escribir(RegistroRef f){
printf("\nSu nombre es >%s<\n", f->nombre);
printf("\nSu primer apellido es >%s<\n", f->ape_1);
printf("\nSu segundo apellido es >%s<\n", f->ape_2);
printf("\nSu dirección es >%s<\n", f->direc);
printf("\nSu teléfono es >%s<\n", f->telef);
printf("\nSu DNI es >%s<\n", f->DNI);
}
/*
Este programa puede dar problemas. De hecho el compilador lo indica al ejecutar:
Por favor, escriba los datos solicitados:
warning: this program uses gets(), which is unsafe.
Por favor, escriba su nombre : Juan
Por favor, escriba su primer apellido : Perez
Por favor, escriba su segundo apellido : Lopez
Por favor, escriba su dirección : Agua, 7
Por favor, escriba su teléfono : 555555555
Por favor, escriba su DNI : 111111111
Los datos introducidos eran como sigue:
Su nombre es >Juan<
Su primer apellido es >Perez<
Su segundo apellido es >Lopez<
Su dirección es >Agua, 7<
Su teléfono es >555555555<
Su DNI es >111111111<
Terminación normal del programa.
*/
Notas
El programa no plantea dificultades al ser alfanuméricos todos los campos. Se utiliza la función
gets()
, contraindicada, para comprobar el peligro que supone. En efecto, basta insertar un nombre (o cualquier otro campo) de longitud superior a la normal para que se produzcan efectos extraños en el mejor de los casos. Los ejercicios siguientes aportan mejores soluciones.
-
Ejercicio 0202r02.-
Construir una estructura de datos adecuada para fichas de alumnos, que contendrá campos para nombre, dos apellidos, dirección, teléfono y DNI . Escribir un programa dotado de funciones que lean y escriba los campos de una variable de este tipo, efectuando comprobaciones de longitud.
La solución puede ser la siguiente:
#include<stdio.h>
#include<string.h>
#include<coti/utiles.h>
#define MAX_PERSONAS 10
typedef struct Registro {
char nombre[20];
char ape_1[20];
char ape_2[20];
char direc[40];
char telef[15];
char DNI[15];
} Registro;
typedef Registro * RegistroRef;
Registro bdd[MAX_PERSONAS];
void leer(RegistroRef);
void escribir(RegistroRef);
int main(int argc, charRef argv[]) {
Registro una_ficha;
printf("\nPor favor, escriba los datos solicitados: \n\n");
leer(&una_ficha);
printf("\n\nLos datos introducidos eran como sigue:\n\n");
escribir(&una_ficha);
printf("\n\nTerminación normal del programa.\n\n");
return 0;
}
void leer(RegistroRef f) {
charRef temp;
temp = pedir("\nPor favor, escriba su nombre : ");
strncpy(f->nombre, temp, sizeof(f->nombre));
free(temp);
printf("\n");
temp = pedir("\nPor favor, escriba su primer apellido : ");
strncpy(f->ape_1, temp, sizeof(f->ape_1));
free(temp);
printf("\n");
temp = pedir("\nPor favor, escriba su segundo apellido : ");
strncpy(f->ape_2, temp, sizeof(f->ape_2));
free(temp);
printf("\n");
temp = pedir("\nPor favor, escriba su dirección : ");
strncpy(f->direc, temp, sizeof(f->direc));
free(temp);
printf("\n");
temp = pedir("\nPor favor, escriba su teléfono : ");
strncpy(f->telef, temp, sizeof(f->telef));
free(temp);
printf("\n");
temp = pedir("\nPor favor, escriba su DNI : ");
strncpy(f->DNI, temp, sizeof(f->DNI));
free(temp);
printf("\n");
}
void escribir(RegistroRef f) {
printf("\nSu nombre es >%s<\n", f->nombre);
printf("\nSu primer apellido es >%s<\n", f->ape_1);
printf("\nSu segundo apellido es >%s<\n", f->ape_2);
printf("\nSu dirección es >%s<\n", f->direc);
printf("\nSu teléfono es >%s<\n", f->telef);
printf("\nSu DNI es >%s<\n", f->DNI);
}
/*
Resultado de la ejecución:
Por favor, escriba los datos solicitados:
Por favor, escriba su nombre : Juan
Por favor, escriba su primer apellido : Pérez
Por favor, escriba su segundo apellido : López
Por favor, escriba su dirección : Agua, 7
Por favor, escriba su teléfono : 55555555
Por favor, escriba su DNI : 11111111
Los datos introducidos eran como sigue:
Su nombre es >Juan<
Su primer apellido es >Pérez<
Su segundo apellido es >López<
Su dirección es >Agua, 7<
Su teléfono es >55555555<
Su DNI es >11111111<
Terminación normal del programa.
*/
Notas
Este ejercicio aporta la función
leer_cadena()
, que resuelve el problema de los desbordamientos matriciales. Se utiliza como base la función
fgets()
, que impone un límite (su segundo argumento) sobre el número de caracteres leídos realmente. En todo caso, se vacía el búfer de teclado una vez efectuada la lectura. Obsérvese que
fgets()
proporciona también, al final de la cadena, el carácter '\n' pulsado por el usuario para denotar el fin de la información. Este carácter puede producir problemas, puesto que realmente no debe formar parte de la información leída, así que lo descartamos asignando el valor '\0', el marcador de fin de cadenas, a la posición
n-1
de la cadena proporcionada. De este modo se cambia el '\n' por un '\0', que es el valor esperado.
-
Ejercicio 0202r03.-
Tomando como base la
struct Registro
anterior, incluir campos de edad y peso. Se pide escribir un programa dotado de funciones que lean y escriba los campos de una variable de este tipo, efectuando comprobaciones de longitud. El programa no admitirá campos vacíos e impondrá una verificación de los valores numéricos.
La solución puede ser la siguiente:
#include<stdio.h>
#include<stdlib.h>
#define MAX_PERSONAS 10
typedef struct Registro {
char nombre[20];
char ape_1[20];
char ape_2[20];
char direc[40];
char telef[15];
char DNI[15];
int edad;
float peso;
} Registro;
typedef Registro * RegistroRef;
Registro bdd[MAX_PERSONAS];
void leer(RegistroRef f);
void escribir(RegistroRef f);
int leer_cadena(charRef p, int longitud_maxima);
int leerint(charRef p, int valor_min, int valor_max);
float leerfloat(charRef p, float valor_min, float valor_max);
double leerdouble(charRef p, double valor_min, double valor_max);
int main(int argc,charRef argv[]) {
Registro una_ficha;
printf("\nPor favor, escriba los datos solicitados:\n\n");
leer(&una_ficha);
printf("\n\nLos datos introducidos eran como sigue:\n\n");
escribir(&una_ficha);
printf("\n\nTerminación normal del programa.\n\n");
return 0;
}
void leer(RegistroRef f){
int temp;
do
{
printf("\nPor favor, escriba su nombre : ");
temp = leer_cadena(f->nombre, sizeof(f->nombre));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su primer apellido : ");
temp = leer_cadena(f->ape_1, sizeof(f->ape_1));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su segundo apellido : ");
temp = leer_cadena(f->ape_2, sizeof(f->ape_2));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su dirección : ");
temp = leer_cadena(f->direc, sizeof(f->direc));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su teléfono : ");
temp = leer_cadena(f->telef, sizeof(f->telef));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su DNI : ");
temp = leer_cadena(f->DNI, sizeof(f->DNI));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
f->edad = leerint("\nPor favor, escriba su edad :",1, 99);
f->peso = leerfloat("\nPor favor, escriba su peso :", 1.0, 120.0);
}
void escribir(RegistroRef f){
printf("\nSu nombre es >%s<\n", f->nombre);
printf("\nSu primer apellido es >%s<\n", f->ape_1);
printf("\nSu segundo apellido es >%s<\n", f->ape_2);
printf("\nSu dirección es >%s<\n", f->direc);
printf("\nSu teléfono es >%s<\n", f->telef);
printf("\nSu DNI es >%s<\n", f->DNI);
printf("\nSu edad es >%d<\n", f->edad);
printf("\nSu peso es >%6.2f<\n", f->peso);
}
int leer_cadena(charRef p, int longitud_maxima)
{
int n;
if (p==NULL || longitud_maxima==0)
return 0;
fgets(p, longitud_maxima, stdin);
fpurge(stdin);
n = strlen(p);
p[n-1] = '\0';
return n;
}
int leerint(charRef p, int valor_min, int valor_max){
char temp[80];
int resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un entero (%d <= n <= %d): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtol(temp, NULL, 10);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
float leerfloat(charRef p, float valor_min, float valor_max){
char temp[80];
float resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un float (%6.2f <= n <= %6.2f): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
double leerdouble(charRef p, double valor_min, double valor_max){
char temp[80];
double resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un double (%6.2lf <= n <= %6.2lf): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
/*
Resultados de la ejecución del programa:
Por favor, escriba los datos solicitados:
Por favor, escriba su nombre :
Perdón, este campo no puede estar vacío.
Por favor, escriba su nombre : José Juan
Por favor, escriba su primer apellido : Martín
Por favor, escriba su segundo apellido : Martín
Por favor, escriba su dirección : 13, Rue de Auteuil
Por favor, escriba su teléfono : 555-555555
Por favor, escriba su DNI : 11111111
Por favor, escriba su edad : 22
Por favor, escriba su peso : 62.8
Los datos introducidos eran como sigue:
Su nombre es >José Juan<
Su primer apellido es >Martín<
Su segundo apellido es >Martín<
Su dirección es >13, Rue de Auteuil<
Su teléfono es >555-555555<
Su DNI es >11111111<
Su edad es >22<
Su peso es > 62.80<
Terminación normal del programa.
*/
Notas
En este ejercicio se han añadido los campos numéricos
edad
y
peso
, que exigirán efectuar una transformación de alfanumérico a binario. Esto se hace mediante las funciones
leerint()
,
leerfloat()
y
leerdouble()
, construidas con este fin. Ambas admiten como argumento una indicación que se mostrará al usuario, y proporcionan como resultado un
int
o un
float
, respectivamente. Además, es preciso indicar a estas funciones el intervalo al que debe pertenecer el valor solicitado. Esto nos permite evitar muchos errores, en especial los asociados al desb ordamiento de listas y matrices, tan frecuente en C. Obsérvese que la función
strtof()
, encargada de traducir del formato alfanumérico al de coma flotante, produce un
double
como resultado. Esto implica la necesidad de transformar el resultado, convirtiéndolo en un
float
. La función
leerdouble()
, desde luego, no requiere esta transformación. Se impone que los campos alfanuméricos no puedan estar vacíos, lo cual se ha logrado haciendo que la función
leer_cadena()
proporcione devuelva la longitud de la cadena proporcionada a través de su primer argumento. Si el usuario pulsa <INTRO> como respuesta a la petición de información efectuada, el resultado será un único carácter (sería un '\n', que hemos convertido en '\0'). Esto se puede detectar en el bucle
while(temp==1)
empleado, y así se garantiza que el usuario inserte al menos una letra como respuesta, antes de pulsar <INTRO>.
-
Ejercicio 0202r04.-
Tomando como base la struct Registro anterior, construir una lista estática de campos (
struct Registro bdd[MAX_PERSONAS]
) y un programa dotado de las opciones y funciones necesarias para dar valor a esos campos y mostrarlos en pantalla con formato encolumnado.
La solución puede ser la siguiente:
#include<stdio.h>
#include<stdlib.h>
#define MAX_PERSONAS 3
typedef struct Registro {
char nombre[15];
char ape_1[15];
char ape_2[15];
char direc[15];
char telef[9];
char DNI[8];
int edad;
float peso;
} Registro;
typedef Registro * RegistroRef;
Registro bdd[MAX_PERSONAS];
int num_columnas[] = {15, 15, 15, 15, 9, 8, 8, 8};
void leer(RegistroRef f);
void escribir(RegistroRef f);
int leer_cadena(charRef p, int longitud_maxima);
float leerfloat(charRef p, float valor_min, float valor_max);
double leerdouble(charRef p, double valor_min, double valor_max);
void escribir_encolumnado(RegistroRef f, intRef p);
int main(int argc,charRef argv[]) {
int i;
printf("\nPor favor, escriba los datos solicitados:\n\n");
for (i=0;i<MAX_PERSONAS;i++)
{
printf("\n\nDatos del registro nº %d:\n\n",i);
leer(&bdd[i]);
}
printf("\n\nLos datos introducidos eran como sigue:\n\n");
for (i=0;i<MAX_PERSONAS;i++)
escribir_encolumnado(&bdd[i], num_columnas);
printf("\n\nTerminación normal del programa.\n\n");
return 0;
}
void leer(RegistroRef f){
int temp;
do
{
printf("\nPor favor, escriba su nombre : ");
temp = leer_cadena(f->nombre, sizeof(f->nombre));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su primer apellido : ");
temp = leer_cadena(f->ape_1, sizeof(f->ape_1));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su segundo apellido : ");
temp = leer_cadena(f->ape_2, sizeof(f->ape_2));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su dirección : ");
temp = leer_cadena(f->direc, sizeof(f->direc));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su teléfono : ");
temp = leer_cadena(f->telef, sizeof(f->telef));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su DNI : ");
temp = leer_cadena(f->DNI, sizeof(f->DNI));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
f->edad = leerint("\nPor favor, escriba su edad :",1, 99);
f->peso = leerfloat("\nPor favor, escriba su peso :", 1.0, 120.0);
}
void escribir_encolumnado(RegistroRef f, intRef p){
/*
La lista señalada por p con tiene las longitudes de los distintos
campos, que se usarán para cortar las cadenas construidas por
concatenación del valor del campo con una cadena de 80 caracteres
llamada relleno.
*/
char relleno[80];
char temp[256];
memset(relleno,' ',79);
relleno[79] = '\0';
strcpy(temp, f->nombre);
strcat(temp, relleno);
temp[p[0]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el primer campo */
strcpy(temp, f->ape_1);
strcat(temp, relleno);
temp[p[1]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el segundo campo */
strcpy(temp, f->ape_2);
strcat(temp, relleno);
temp[p[2]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el tercer campo */
strcpy(temp, f->direc);
strcat(temp, relleno);
temp[p[3]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el cuarto campo */
strcpy(temp, f->telef);
strcat(temp, relleno);
temp[p[4]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el quinto campo */
strcpy(temp, f->DNI);
strcat(temp, relleno);
temp[p[5]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el sexto campo */
sprintf(temp,"%8.2d", f->edad);
printf("%s", temp);
sprintf(temp,"%8.2f", f->peso);
printf("%s\n", temp);
}
int leer_cadena(charRef p, int longitud_maxima)
{
int n;
if (p==NULL || longitud_maxima==0)
return 0;
fgets(p, longitud_maxima, stdin);
fpurge(stdin);
n = strlen(p);
p[n-1] = '\0';
return n;
}
int leerint(charRef p, int valor_min, int valor_max){
char temp[80];
int resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un entero (%d <= n <= %d): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtol(temp, NULL, 10);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
float leerfloat(charRef p, float valor_min, float valor_max){
char temp[80];
float resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un float (%6.2f <= n <= %6.2f): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
double leerdouble(charRef p, double valor_min, double valor_max){
char temp[80];
double resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un double (%6.2lf <= n <= %6.2lf): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
/*
Por favor, escriba los datos solicitados:
Datos del registro nº 0:
Por favor, escriba su nombre : Mateo
Por favor, escriba su primer apellido : Mateo
Por favor, escriba su segundo apellido : Mateo
Por favor, escriba su dirección : Rue de Mateo
Por favor, escriba su teléfono : 1111
Por favor, escriba su DNI : 1111
Por favor, escriba su edad : 11
Por favor, escriba su peso : 11
Datos del registro nº 1:
Por favor, escriba su nombre : Marcos
Por favor, escriba su primer apellido : Marcos
Por favor, escriba su segundo apellido : Marcos
Por favor, escriba su dirección : Rue de Marcos
Por favor, escriba su teléfono : 22
Por favor, escriba su DNI : 22
Por favor, escriba su edad : 22
Por favor, escriba su peso : 22
Datos del registro nº 2:
Por favor, escriba su nombre : Lucas
Por favor, escriba su primer apellido : Lucas
Por favor, escriba su segundo apellido : Lucas
Por favor, escriba su dirección : Rue de Lucas
Por favor, escriba su teléfono : 33
Por favor, escriba su DNI : 33
Por favor, escriba su edad : 33
Por favor, escriba su peso : 33
Los datos introducidos eran como sigue:
Mateo Mateo Mateo Rue de Mateo 1111 1111 11 11.00
Marcos Marcos Marcos Rue de Marcos 22 22 22 22.00
Lucas Lucas Lucas Rue de Lucas 33 33 33 33.00
Terminación normal del programa.
*/
Notas
Este programa aporta novedades significativas. En primer lugar, se ha construido una lista de registros, en lugar de emplera una única variable. Esto permitiría, mediante una constante
MAX_PERSONAS
adecuada, disponer de gran cantidad de registros. Por otra parte, se ha construido una lista,
num_columnas
, que contiene las longitudes deseadas para los campos cuando éstos se impriman en pantalla. La importancia de los informes en columnas es patente, y se emplearán con frecuencia en el tratamiento de ficheros, en la
sección correspondiente
. Se ha creado, por tanto, un método llamado
escribir_encolumnado()
que permite mostra en pantalla un registro con las especificaciones de longitud de campos indicadas. La estructura del programa es sencilla y plantea dificultades sin por alguna razón cambiase la implementación de la estructura de datos empleada. Esto es un problema grave, que exigiría hacer cambios en muchos lugares del programa. Es muy preferible recurrir a pequeñas funciones adecuadas para acceder a la base de datos, construidas y empleadas de modo que los cambios habidos en la implementación afecten únicamente a unos pocos puntos (unas pocas funciones, claro) del programa. Si mantenemos constantes las signaturas de todas las funciones (el número, tipo y orden de sus argumentos), podemos estar seguros de que los cambios habidos en las estructuras de datos subyacentes no afectarán
para nada
al resto del programa. Esto supone un enorme ahorro de trabajo.
El procedimiento empleado para construir campos de longitud predeterminada, sin duda mejorable, consiste en añadir (mediante
strcat()
) una colección de espacios en blanco a una cadena que contiene inicialment el valor del campo en cuestión. Una vez creada esa cadena temporal formada por el valor del campo y el "relleno" de espacios, se inserta un marcador de fin de cadena ('\0') en el lugar que convenga para así definir la longitud práctica de
temp
. Los trozos se van imprimiendo en pantalla a medida que se construyen, aunque nada impediría añadirlos a una cadena "almacén" que se imprimiría finalmente en pantalla o en disco. Resulta imprescindible la especificación de longitudes de los campos, almacenadas en
num_columnas
y pasadas a
escribir_encolumnado()
como argumento.
Obsérvese también el uso de la función
sprintf()
para crear cadenas que contienen los campos de edad y de peso, y que se añaden oportunamente a lo ya expuesto en pantalla. Una vez más, nada impediría añadir esta información a una cadena global que finalmente se enviaría a pantalla o a un archivo de disco.
-
Ejercicio 0202r05.-
Construir una base de datos basada en la
struct Registro bdd[MAX_PERSONAS]
anterior. El programa debe permitir la realización de altas, bajas y modificaciones.
La solución puede ser la siguiente:
| main.c |
/*
* main.c
*
* Created on: 03/09/2009
* Author: coti
*/
#include<stdio.h>
#include<stdlib.h>
#include<coti/utiles.h>
#include "tui.h"
int main(int argc, charRef argv[]) {
Registro bdd[MAX_PERSONAS];
int num_columnas[] = { 15, 15, 15, 15, 9, 8, 8, 8 };
enum {
Altas, Bajas, Modificaciones, Ver, Salir
};
char menu[][20] = { "Altas", "Bajas", "Modificaciones", "Ver registros",
"Salir" };
char opcion;
int i, caso;
iniciacion(bdd);
do {
/* Visualizar menú y leer la opción */
printf("\n");
for (i = 0; i < NUM_OPCIONES; i++)
printf(" (%d) %s", i, menu[i]);
printf(" : ");
scanf("%c%*c", &opcion);
fpurge(stdin);
/* Mostrar en pantalla la opción seleccionada */
caso = opcion - '0';
switch (caso) {
case Altas:
dar_altas(bdd);
break;
case Bajas:
dar_bajas(bdd);
break;
case Modificaciones:
hacer_modificaciones(bdd);
break;
case Ver:
ver(bdd, num_columnas);
break;
case Salir:
printf("\nSaliendo...%s\n", menu[caso]);
break;
default:
printf("\n\nOpción incorrecta\n\n");
break;
}
} while (caso != Salir);
printf("\n\nFin del programa\n\n");
return 0;
}
|
| tui.h |
/*
* tui.h
*
* Created on: 04/09/2009
* Author: coti
*/
#ifndef TUI_H_
#define TUI_H_
#include<coti/utiles.h>
#define MAX_PERSONAS 3
#define NUM_OPCIONES 5
typedef struct Registro {
char nombre[15];
char ape_1[15];
char ape_2[15];
char direc[15];
char telef[9];
char DNI[8];
int edad;
float peso;
} Registro;
typedef Registro * RegistroRef;
void leer(RegistroRef f);
void escribir_encolumnado(RegistroRef f, intRef p);
void iniciacion(RegistroRef f);
void ver(RegistroRef f, intRef nc);
void dar_altas(RegistroRef f);
void dar_bajas(RegistroRef f);
void hacer_modificaciones(RegistroRef f);
#endif /* TUI_H_ */
|
| tui.c |
/*
* tui.c
*
* Created on: 04/09/2009
* Author: coti
*/
#include "tui.h"
void iniciacion(RegistroRef f) {
int i;
for (i = 0; i < MAX_PERSONAS; i++)
strcpy(f[i].nombre, "*");
}
void ver(RegistroRef f, intRef nc) {
int i;
printf("\n\nEl contenido de los registros ocupados es como sigue:\n\n");
for (i = 0; i < MAX_PERSONAS; i++)
if (f[i].nombre[0] != '*')
escribir_encolumnado(f + i, nc);
}
void dar_altas(RegistroRef f) {
int i, n, num_disponibles_para_alta = 0;
printf("\n\nProceso de altas\n\n");
for (i = 0; i < MAX_PERSONAS; i++)
if (f[i].nombre[0] == '*') {
printf("El registro %d puede causar alta.\n", i);
num_disponibles_para_alta++;
}
if (num_disponibles_para_alta == 0) {
printf("\nNo hay registros disponibles para la alta.");
return;
}
do {
n = pedirInt("Escriba el número del registro que quiere rellenar: ", 0,
MAX_PERSONAS - 1);
} while (f[n].nombre[0] != '*');
leer(&f[n]);
}
void dar_bajas(RegistroRef f) {
int i, n, m, num_disponibles_para_baja = 0;
printf("\n\nProceso de bajas\n\n");
for (i = 0; i < MAX_PERSONAS; i++)
if (f[i].nombre[0] != '*') {
printf("El registro %d puede causar baja.\n", i);
num_disponibles_para_baja++;
}
if (num_disponibles_para_baja == 0) {
printf("\nNo hay registros disponibles para la baja.");
return;
}
do {
n = pedirInt("Escriba el número del registro que quiera eliminar"
" (confirmación posterior): ", 0, MAX_PERSONAS - 1);
} while (f[n].nombre[0] == '*');
m = confirmar("¿Desea realmente dar de baja este registro?");
if (m)
strcpy(f[n].nombre, "*");
else
printf("\nNo se ha efectuado la baja\n");
}
void leer(RegistroRef f) {
charRef temp;
temp = pedir_nv("\nPor favor, escriba su nombre : ");
strncpy(f->nombre, temp, sizeof(f->nombre));
free(temp);
temp = pedir_nv("\nPor favor, escriba su primer apellido : ");
strncpy(f->ape_1, temp, sizeof(f->ape_1));
free(temp);
temp = pedir_nv("\nPor favor, escriba su segundo apellido : ");
strncpy(f->ape_2, temp, sizeof(f->ape_2));
free(temp);
temp = pedir_nv("\nPor favor, escriba su dirección : ");
strncpy(f->direc, temp, sizeof(f->direc));
free(temp);
temp = pedir_nv("\nPor favor, escriba su teléfono : ");
strncpy(f->telef, temp, sizeof(f->telef));
free(temp);
temp = pedir("\nPor favor, escriba su DNI : ");
strncpy(f->DNI, temp, sizeof(f->DNI));
free(temp);
f->edad = pedirInt("\nPor favor, escriba su edad :", 1, 99);
f->peso = pedirFloat("\nPor favor, escriba su peso :", 1.0,
120.0);
}
void escribir_encolumnado(RegistroRef f, intRef p) {
/*
La lista señalada por p con tiene las longitudes de los distintos
campos, que se usarán para cortar las cadenas construidas por
concatenación del valor del campo con una cadena de 80 caracteres
llamada relleno.
*/
char relleno[80];
char temp[256];
memset(relleno, ' ', 79);
relleno[79] = '\0';
strcpy(temp, f->nombre);
strcat(temp, relleno);
temp[p[0]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el primer campo */
strcpy(temp, f->ape_1);
strcat(temp, relleno);
temp[p[1]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el segundo campo */
strcpy(temp, f->ape_2);
strcat(temp, relleno);
temp[p[2]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el tercer campo */
strcpy(temp, f->direc);
strcat(temp, relleno);
temp[p[3]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el cuarto campo */
strcpy(temp, f->telef);
strcat(temp, relleno);
temp[p[4]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el quinto campo */
strcpy(temp, f->DNI);
strcat(temp, relleno);
temp[p[5]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el sexto campo */
sprintf(temp, "%8d", f->edad);
printf("%s", temp);
sprintf(temp, "%8.2f", f->peso);
printf("%s\n", temp);
}
void hacer_modificaciones(RegistroRef f) {
int i, n, m, num_disponibles_para_modificaciones = 0;
Registro temp;
printf("\n\nProceso de modificaciones\n\n");
for (i = 0; i < MAX_PERSONAS; i++)
if (f[i].nombre[0] != '*') {
printf("El registro %d puede ser modificado.\n", i);
num_disponibles_para_modificaciones++;
}
if (num_disponibles_para_modificaciones == 0) {
printf("\nNo hay registros disponibles para hacer modificaciones.");
return;
}
do {
n = pedirInt("Escriba el número del registro que quiera modificar: ",
0, MAX_PERSONAS - 1);
} while (f[n].nombre[0] == '*');
printf("\nIntroduzca los nuevos datos (confirmación posterior):\n\n");
leer(&temp);
m = confirmar("Desea guardar los cambios?");
if (m)
f[n] = temp;
else
printf("\nModificaciones descartadas\n");
}
|
/*
Resultado de la ejecución del programa:
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 0
Proceso de altas
El registro 0 puede causar alta.
El registro 1 puede causar alta.
El registro 2 puede causar alta.
Escriba el número del registro que quiere rellenar: 0
Por favor, escriba su nombre : Juan José
Por favor, escriba su primer apellido : Pérez
Por favor, escriba su segundo apellido : López
Por favor, escriba su dirección : 13, Rue des Oiseaux
Por favor, escriba su teléfono : 555-5555
Por favor, escriba su DNI : 555555555
Por favor, escriba su edad : 55
Por favor, escriba su peso : 55
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 0
Proceso de altas
El registro 1 puede causar alta.
El registro 2 puede causar alta.
Escriba el número del registro que quiere rellenar: 2
Por favor, escriba su nombre : José
Por favor, escriba su primer apellido : García
Por favor, escriba su segundo apellido : García
Por favor, escriba su dirección : Calle del Pez, 13
Por favor, escriba su teléfono : 666-666666
Por favor, escriba su DNI : 666666666
Por favor, escriba su edad : 66
Por favor, escriba su peso : 66
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 3
El contenido de los registros ocupados es como sigue:
Juan José Pérez López 13, Rue des O 555-555 555555 55 55.00
José García García Calle del Pez 666-666 666666 66 66.00
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 2
Proceso de modificaciones
El registro 0 puede ser modificado.
El registro 2 puede ser modificado.
Escriba el número del registro que quiera modificar: 2
Introduzca los nuevos datos (confirmación posterior):
Por favor, escriba su nombre : José R.
Por favor, escriba su primer apellido : Pérez
Por favor, escriba su segundo apellido : García
Por favor, escriba su dirección : 13, Rue d'O
Por favor, escriba su teléfono : 666-666666666
Por favor, escriba su DNI : 66666666
Por favor, escriba su edad : 57
Por favor, escriba su peso : 56
Escriba 1 para confirmar cambios, 2 para descartar: 1
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 2
Proceso de modificaciones
El registro 0 puede ser modificado.
El registro 1 puede ser modificado.
El registro 2 puede ser modificado.
Escriba el número del registro que quiera modificar: 3
Escriba el número del registro que quiera modificar: 2
Introduzca los nuevos datos (confirmación posterior):
Por favor, escriba su nombre : L
Por favor, escriba su primer apellido : L
Por favor, escriba su segundo apellido : L
Por favor, escriba su dirección : L
Por favor, escriba su teléfono : L
Por favor, escriba su DNI : L
Por favor, escriba su edad : 1
Por favor, escriba su peso : 1
Escriba 1 para confirmar cambios, 2 para descartar: 2
Modificaciones descartadas
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 3
El contenido de los registros ocupados es como sigue:
Juan José Pérez López 13, Rue des O 555-555 555555 55 55.00
José R. Pérez García 13, Rue d'O 666-666 666666 57 56.00
José García García Calle del Pez 666-666 666666 66 66.00
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 1
Proceso de bajas
El registro 0 puede causar baja.
El registro 1 puede causar baja.
El registro 2 puede causar baja.
Escriba el número del registro que quiera eliminar (confirmación posterior): 2
Escriba 1 para confirmar la baja, 2 para descartar: 1
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 3
El contenido de los registros ocupados es como sigue:
Juan José Pérez López 13, Rue des O 555-555 555555 55 55.00
José R. Pérez García 13, Rue d'O 666-666 666666 57 56.00
Altas (0) Bajas (1) Modificaciones (2) Ver registros (3) Salir (4) : 4
Saliendo...
Fin del programa
*/
Notas
Este programa es algo más ambicioso, y muestra una sencilla base de datos con las posibilidades habituales. Como puede observarse, los detalles de implementación se extienden a todas las funciones, lo cual supondría la necesidad d ehacer grandes cambios si se modificarla la implementación.
El proceso de altas, bajas y modificaciones se basa en insertar un asterisco en el campo de nombre del registro en cuestión: estos registros se consideran disponibles para almacenar información; los otros sólo pueden ser dados de baja o modificados. Se garantiza que no sea posible dar de alta un registro ocupado, ni dar de baja un registro vacío. Tanto las altas como las bajas se realizan a través de la función
leer()
, que admite como argumento un puntero de registro. Adicionalmente, se permite abortar los procesos de baja, por si el usuario cometiera un error. En ambos procesos se ofrece una lista formada por los números de los registros disponibles en uno en otro sentido (vacíos, para dar altas, o llenos, para dar bajas). Las modificaciones se realizan con un procedimiento análogo a las altas; una vez introducidos los datos, se ofrece al usuario la posibilidad de descartar las modificaciones.
Se muestra el resultado de una ejecución del programa; se observará que entran en acción todos los mecanismos de protección implantados (no se admiten campos alfanuméricos vacíos, se cortan los caracteres sobrantes, se comprueban los números de los registros, etc). Se ha hecho uso de una lista estática de registros, según lo indicado.
-
Ejercicio 0202r06.-
Construir un programa como el anterior, basado esta vez en una lista estática de punteros de estructuras (
RegistroRef bdd[MAX_PERSONAS]
). El programa dará inicialmente el valor
NULL
a todos los punteros de la lista; se considera que un registro está vacío cuando su puntero es nulo. Las altas reservarán dinámicamente memoria para un registro (con
malloc
o
calloc
) y las bajas eliminarán realmente el registro (mediante
free
).
La solución puede ser la siguiente:
#include<stdio.h>
#include<stdlib.h>
#define MAX_PERSONAS 3
#define NUM_OPCIONES 5
typedef struct Registro {
char nombre[15];
char ape_1[15];
char ape_2[15];
char direc[15];
char telef[9];
char DNI[8];
int edad;
float peso;
} Registro;
typedef Registro * RegistroRef;
RegistroRef bdd[MAX_PERSONAS];
/*
Las funciones siguientes permiten determinar, respectivamente,
si un registro está vacío o no, y el puntero de cualquier
registro de la base de datos. Dependen de la implementación,
pero si cambia la implementación no varía la signatura de las
funciones y por tanto no varía la estructura del programa.
Cambia la implementación.
No cambia el programa.
*/
int registro_vacio(int i);
RegistroRef pdr(int i);
void llenar_registro(int n);
int leer_cadena(charRef p, int longitud_maxima);
float leerfloat(charRef p, float valor_min, float valor_max);
double leerdouble(charRef p, double valor_min, double valor_max);
void leer(RegistroRef f);
void escribir_encolumnado(RegistroRef f, intRef p);
void iniciacion();
void ver(intRef nc);
void dar_altas();
void dar_bajas();
void hacer_modificaciones();
int main(int argc, charRef argv[])
{
int num_columnas[] = {15, 15, 15, 15, 9, 8, 8, 8};
enum {Altas, Bajas, Modificaciones, Ver, Salir};
char menu[][20] = {"Altas", "Bajas", "Modificaciones", "Ver registros", "Salir"};
char opcion;
int i, caso;
iniciacion();
do
{
/* Visualizar menú y leer la opción */
printf("\n");
for(i=0;i<NUM_OPCIONES;i++)
printf("%s (%d) ", menu[i], i);
printf(" : ");
scanf("%c", &opcion);
fpurge(stdin);
/* Mostrar en pantalla la opción seleccionada */
caso = opcion - '0';
switch(caso)
{
case Altas: dar_altas();
break;
case Bajas: dar_bajas();
break;
case Modificaciones: hacer_modificaciones();
break;
case Ver: ver(num_columnas);
break;
case Salir: printf("\nSaliendo...\n", menu[caso]);
break;
default: printf("\n\nOpción incorrecta\n\n");
break;
}
}
while(caso != Salir);
printf("\n\nFin del programa\n\n");
return 0;
}
/*
Las funciones
registro_vacio()
pdr()
iniciacion()
dependen directamente de la estructura de datos empleada
en la implementación. Si cambia la estructura de datos,
cambia la implementación de estas funciones.
Pero su signatura no cambia!
*/
int registro_vacio(int i){
return bdd[i] == NULL;
}
RegistroRef pdr(int i){
return bdd[i];
}
void llenar_registro(int n)
{
RegistroRef temp;
temp = malloc(sizeof(struct Registro));
if (temp)
{
leer(temp);
bdd[n] = temp;
}
}
void vaciar_registro(int n)
{
free(bdd[n]);
bdd[n] = NULL;
}
void iniciacion() {
int i;
for(i=0;i<MAX_PERSONAS;i++)
bdd[i] = NULL;
}
void ver(intRef nc)
{
int i;
int ocupados = 0;
printf("\n\nListado de la base de datos:\n\n");
for (i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
ocupados++;
escribir_encolumnado(pdr(i), nc);
}
if (ocupados == 0)
printf("\nTodos los registros están vacíos.\n\n");
else
printf("\nHay %d registros ocupados.\n\n", ocupados);
}
void dar_altas()
{
int i, n, num_disponibles_para_alta = 0;
printf("\n\nProceso de altas\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (registro_vacio(i))
{
printf("El registro %d puede causar alta.\n", i);
num_disponibles_para_alta++;
}
if (num_disponibles_para_alta == 0)
{
printf("\nNo hay registros disponibles para la alta.");
return;
}
do
{
printf("Escriba el número del registro que quiere rellenar: ");
scanf("%d", &n);
fpurge(stdin);
} while (!registro_vacio(n));
llenar_registro(n);
}
void dar_bajas() {
int i, n, m, num_disponibles_para_baja = 0;
printf("\n\nProceso de bajas\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
printf("El registro %d puede causar baja.\n", i);
num_disponibles_para_baja ++;
}
if (num_disponibles_para_baja == 0)
{
printf("\nNo hay registros disponibles para la baja.");
return;
}
do
{
printf("Escriba el número del registro que quiera eliminar (confirmación posterior): ");
scanf("%d", &n);
fpurge(stdin);
} while (registro_vacio(n));
do
{
printf("Escriba 1 para confirmar la baja, 2 para descartar: ");
scanf("%d", &m);
fpurge(stdin);
} while (m != 1 && m != 2);
if (m == 1)
{
vaciar_registro(n);
}
else
printf("\nNo se ha efectuado la baja\n");
}
void leer(RegistroRef f){
int temp;
do
{
printf("\nPor favor, escriba su nombre : ");
temp = leer_cadena(f->nombre, sizeof(f->nombre));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su primer apellido : ");
temp = leer_cadena(f->ape_1, sizeof(f->ape_1));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su segundo apellido : ");
temp = leer_cadena(f->ape_2, sizeof(f->ape_2));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su dirección : ");
temp = leer_cadena(f->direc, sizeof(f->direc));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su teléfono : ");
temp = leer_cadena(f->telef, sizeof(f->telef));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su DNI : ");
temp = leer_cadena(f->DNI, sizeof(f->DNI));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
f->edad = leerint("\nPor favor, escriba su edad :",1, 99);
f->peso = leerfloat("\nPor favor, escriba su peso :", 1.0, 120.0);
}
void escribir_encolumnado(RegistroRef f, intRef p){
/*
La lista señalada por p con tiene las longitudes de los distintos
campos, que se usarán para cortar las cadenas construidas por
concatenación del valor del campo con una cadena de 80 caracteres
llamada relleno.
*/
char relleno[80];
char temp[256];
memset(relleno,' ',79);
relleno[79] = '\0';
strcpy(temp, f->nombre);
strcat(temp, relleno);
temp[p[0]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el primer campo */
strcpy(temp, f->ape_1);
strcat(temp, relleno);
temp[p[1]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el segundo campo */
strcpy(temp, f->ape_2);
strcat(temp, relleno);
temp[p[2]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el tercer campo */
strcpy(temp, f->direc);
strcat(temp, relleno);
temp[p[3]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el cuarto campo */
strcpy(temp, f->telef);
strcat(temp, relleno);
temp[p[4]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el quinto campo */
strcpy(temp, f->DNI);
strcat(temp, relleno);
temp[p[5]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el sexto campo */
sprintf(temp,"%8d", f->edad);
printf("%s", temp);
sprintf(temp,"%8.2f", f->peso);
printf("%s\n", temp);
}
int leer_cadena(charRef p, int longitud_maxima)
{
int n;
if (p==NULL || longitud_maxima==0)
return 0;
fgets(p, longitud_maxima, stdin);
fpurge(stdin);
n = strlen(p);
p[n-1] = '\0';
return n;
}
int leerint(charRef p, int valor_min, int valor_max){
char temp[80];
int resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un entero (%d <= n <= %d): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtol(temp, NULL, 10);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
float leerfloat(charRef p, float valor_min, float valor_max){
char temp[80];
float resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un float (%6.2f <= n <= %6.2f): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
double leerdouble(charRef p, double valor_min, double valor_max){
char temp[80];
double resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un double (%6.2lf <= n <= %6.2lf): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
void hacer_modificaciones(){
int i, n, m, num_disponibles_para_modificaciones = 0;
struct Registro temp;
printf("\n\nProceso de modificaciones\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
printf("El registro %d puede ser modificado.\n", i);
num_disponibles_para_modificaciones++;
}
if (num_disponibles_para_modificaciones == 0)
{
printf("\nNo hay registros disponibles para hacer modificaciones.");
return;
}
do
{
printf("Escriba el número del registro que quiera modificar: ");
scanf("%d", &n);
fpurge(stdin);
} while (registro_vacio(n) || n<0 || n>=MAX_PERSONAS);
printf("\nIntroduzca los nuevos datos (confirmación posterior):\n\n");
leer(&temp);
do
{
printf("Escriba 1 para confirmar cambios, 2 para descartar: ");
scanf("%d", &m);
fpurge(stdin);
} while (m != 1 && m != 2);
if (m == 1)
*pdr(n) = temp;
else
printf("\nModificaciones descartadas\n");
}
Notas
Este programa es, en principio, una ligera modificación del anterior. En efecto, se trata de cambiar la implementación de la estructura de datos, pasando de una lista de estructuras a una lista de punteros de estructura. Los cambios efectuados son considerables, porque se ha recurrido al uso de varias funciones que actúan como intermediarios entre las necesidades del programa y la implementación interna usada para almacenar los datos. En este sentido, las funciones
registro_vacio()
,
pdr()
y
llenar_registro()
se han utilizado como primitivas en todas las demás funciones. Además, se ha pasado a utilizar una variable global, que emplean las tres funciones anteriores a instancias de todas las demás. El objetivo perseguido es aislar lo cambios que produciría un cambio en la estructura de datos básica. De no hacerse así, un cambio de implementación de esta estructura produciría cambios en todo el programa.
Obsérvese que se han eliminado argumentos en varias funciones, al hacerlos innecesarios el uso de las primitivas de acceso indicadas anteriormente. Con esto se pretende evitar cambios en las signaturas y por tanto aislar los cambios en zonas concretas del programa.
Las funciones
registro_vacio()
,
pdr()
,
llenar_registro()
y vaciar_registro() dependen necesariamente de la implementación, pero ahi acaba el problema: todas las demás funciones se pueden construir empleando a las anteriores como primitivas. En términos de la metodología orientada a objetos, el uso de métodos de acceso aísla a los métodos de los atributos. El comportamiento del programa es totalmente idéntico al obtenido en el ejercicio anterior.
-
Ejercicio 0202r07.-
Modificar el programa anterior para que haga uso de una lista dinámica de estructuras (
bdd
es un bloque formado por estructuras y reservado dinámicamente).
La solución puede ser la siguiente:
#include<stdio.h>
#include<stdlib.h>
#define MAX_PERSONAS 3
#define NUM_OPCIONES 5
struct Registro {
char nombre[15];
char ape_1[15];
char ape_2[15];
char direc[15];
char telef[9];
char DNI[8];
int edad;
float peso;
} Registro;
typedef Registro * RegistroRef;
RegistroRef bdd;
/*
Las funciones siguientes permiten determinar, respectivamente,
si un registro está vacío o no, y el puntero de cualquier
registro de la base de datos. Dependen de la implementación,
pero si cambia la implementación no varía la signatura de las
funciones y por tanto no varía la estructura del programa.
Cambia la implementación.
No cambia el programa.
*/
int registro_vacio(int i);
RegistroRef pdr(int i);
void llenar_registro(int n);
int leer_cadena(charRef p, int longitud_maxima);
float leerfloat(charRef p, float valor_min, float valor_max);
double leerdouble(charRef p, double valor_min, double valor_max);
void leer(RegistroRef f);
void escribir_encolumnado(RegistroRef f, intRef p);
void iniciacion();
void ver(intRef nc);
void dar_altas();
void dar_bajas();
void hacer_modificaciones();
int main(int argc, charRef argv[])
{
int num_columnas[] = {15, 15, 15, 15, 9, 8, 8, 8};
enum {Altas, Bajas, Modificaciones, Ver, Salir};
char menu[][20] = {"Altas", "Bajas", "Modificaciones", "Ver registros", "Salir"};
char opcion;
int i, caso;
iniciacion();
do
{
/* Visualizar menú y leer la opción */
printf("\n");
for(i=0;i<NUM_OPCIONES;i++)
printf("%s (%d) ", menu[i], i);
printf(" : ");
scanf("%c", &opcion);
fpurge(stdin);
/* Mostrar en pantalla la opción seleccionada */
caso = opcion - '0';
switch(caso)
{
case Altas: dar_altas();
break;
case Bajas: dar_bajas();
break;
case Modificaciones: hacer_modificaciones();
break;
case Ver: ver(num_columnas);
break;
case Salir: printf("\nSaliendo...\n", menu[caso]);
break;
default: printf("\n\nOpción incorrecta\n\n");
break;
}
}
while(caso != Salir);
printf("\n\nFin del programa\n\n");
return 0;
}
/*
Las funciones
registro_vacio()
pdr()
iniciacion()
dependen directamente de la estructura de datos empleada
en la implementación. Si cambia la estructura de datos,
cambia la implementación de estas funciones.
Pero su signatura no cambia!
*/
int registro_vacio(int i){
return bdd[i].nombre[0] == '*';
}
RegistroRef pdr(int i){
return bdd+i;
}
void llenar_registro(int n)
{
struct Registro temp;
leer(&temp);
bdd[n] = temp;
}
void vaciar_registro(int n)
{
strcpy(bdd[n].nombre, "*");
}
void iniciacion() {
int i;
bdd = malloc(sizeof(struct Registro)*MAX_PERSONAS);
for(i=0; i<MAX_PERSONAS;i++)
strcpy(bdd[i].nombre, "*");
}
/* Las demás funciones están construidas tomando como base a las cuatro
anteriores, luego no se ven afectadas por los cambios efectuados en la
implementación.
*/
void ver(intRef nc)
{
int i;
int ocupados = 0;
printf("\n\nListado de la base de datos:\n\n");
for (i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
ocupados++;
escribir_encolumnado(pdr(i), nc);
}
if (ocupados == 0)
printf("\nTodos los registros están vacíos.\n\n");
else
printf("\nHay %d registros ocupados.\n\n", ocupados);
}
void dar_altas()
{
int i, n, num_disponibles_para_alta = 0;
printf("\n\nProceso de altas\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (registro_vacio(i))
{
printf("El registro %d puede causar alta.\n", i);
num_disponibles_para_alta++;
}
if (num_disponibles_para_alta == 0)
{
printf("\nNo hay registros disponibles para la alta.");
return;
}
do
{
printf("Escriba el número del registro que quiere rellenar: ");
scanf("%d", &n);
fpurge(stdin);
} while (!registro_vacio(n));
llenar_registro(n);
}
void dar_bajas() {
int i, n, m, num_disponibles_para_baja = 0;
printf("\n\nProceso de bajas\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
printf("El registro %d puede causar baja.\n", i);
num_disponibles_para_baja ++;
}
if (num_disponibles_para_baja == 0)
{
printf("\nNo hay registros disponibles para la baja.");
return;
}
do
{
printf("Escriba el número del registro que quiera eliminar (confirmación posterior): ");
scanf("%d", &n);
fpurge(stdin);
} while (registro_vacio(n));
do
{
printf("Escriba 1 para confirmar la baja, 2 para descartar: ");
scanf("%d", &m);
fpurge(stdin);
} while (m != 1 && m != 2);
if (m == 1)
{
vaciar_registro(n);
}
else
printf("\nNo se ha efectuado la baja\n");
}
void leer(RegistroRef f){
int temp;
do
{
printf("\nPor favor, escriba su nombre : ");
temp = leer_cadena(f->nombre, sizeof(f->nombre));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su primer apellido : ");
temp = leer_cadena(f->ape_1, sizeof(f->ape_1));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su segundo apellido : ");
temp = leer_cadena(f->ape_2, sizeof(f->ape_2));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su dirección : ");
temp = leer_cadena(f->direc, sizeof(f->direc));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su teléfono : ");
temp = leer_cadena(f->telef, sizeof(f->telef));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
do
{
printf("\nPor favor, escriba su DNI : ");
temp = leer_cadena(f->DNI, sizeof(f->DNI));
if (temp==1)
printf("\nPerdón, este campo no puede estar vacío.\n");
} while(temp==1);
f->edad = leerint("\nPor favor, escriba su edad :",1, 99);
f->peso = leerfloat("\nPor favor, escriba su peso :", 1.0, 120.0);
}
void escribir_encolumnado(RegistroRef f, intRef p){
/*
La lista señalada por p con tiene las longitudes de los distintos
campos, que se usarán para cortar las cadenas construidas por
concatenación del valor del campo con una cadena de 80 caracteres
llamada relleno.
*/
char relleno[80];
char temp[256];
memset(relleno,' ',79);
relleno[79] = '\0';
strcpy(temp, f->nombre);
strcat(temp, relleno);
temp[p[0]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el primer campo */
strcpy(temp, f->ape_1);
strcat(temp, relleno);
temp[p[1]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el segundo campo */
strcpy(temp, f->ape_2);
strcat(temp, relleno);
temp[p[2]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el tercer campo */
strcpy(temp, f->direc);
strcat(temp, relleno);
temp[p[3]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el cuarto campo */
strcpy(temp, f->telef);
strcat(temp, relleno);
temp[p[4]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el quinto campo */
strcpy(temp, f->DNI);
strcat(temp, relleno);
temp[p[5]] = '\0'; /* Se especifica la posición del fin de cadena */
printf("%s", temp);/* Se imprime en pantalla el sexto campo */
sprintf(temp,"%8d", f->edad);
printf("%s", temp);
sprintf(temp,"%8.2f", f->peso);
printf("%s\n", temp);
}
int leer_cadena(charRef p, int longitud_maxima)
{
int n;
if (p==NULL || longitud_maxima==0)
return 0;
fgets(p, longitud_maxima, stdin);
fpurge(stdin);
n = strlen(p);
p[n-1] = '\0';
return n;
}
int leerint(charRef p, int valor_min, int valor_max){
char temp[80];
int resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un entero (%d <= n <= %d): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtol(temp, NULL, 10);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
float leerfloat(charRef p, float valor_min, float valor_max){
char temp[80];
float resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un float (%6.2f <= n <= %6.2f): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
double leerdouble(charRef p, double valor_min, double valor_max){
char temp[80];
double resultado;
do
{
if (p!=NULL)
printf("%s ", p);
else
printf("Por favor, escriba un double (%6.2lf <= n <= %6.2lf): ", valor_min, valor_max);
fgets(temp, 80, stdin);
fpurge(stdin);
resultado = strtof(temp, NULL);
} while (resultado < valor_min || resultado > valor_max);
return resultado;
}
void hacer_modificaciones(){
int i, n, m, num_disponibles_para_modificaciones = 0;
struct Registro temp;
printf("\n\nProceso de modificaciones\n\n");
for(i=0;i<MAX_PERSONAS;i++)
if (!registro_vacio(i))
{
printf("El registro %d puede ser modificado.\n", i);
num_disponibles_para_modificaciones++;
}
if (num_disponibles_para_modificaciones == 0)
{
printf("\nNo hay registros disponibles para hacer modificaciones.");
return;
}
do
{
printf("Escriba el número del registro que quiera modificar: ");
scanf("%d", &n);
fpurge(stdin);
} while (registro_vacio(n) || n<0 || n>=MAX_PERSONAS);
printf("\nIntroduzca los nuevos datos (confirmación posterior):\n\n");
leer(&temp);
do
{
printf("Escriba 1 para confirmar cambios, 2 para descartar: ");
scanf("%d", &m);
fpurge(stdin);
} while (m != 1 && m != 2);
if (m == 1)
*pdr(n) = temp;
else
printf("\nModificaciones descartadas\n");
}
Notas
En este programa se solicita modificar las estructuras básicas de datos empleadas en el programa anterior, y se recogen claro está los beneficios derivados del uso de funciones de acceso. La estructura del programa queda totalmente intacta, y basta concentrar nuestros esfuerzos en unas pocas funciones que dependen realmente de la implementación, y que se emplean como primitivas en todas las demás. De este modo los cambios quedan restringidos a lo mínino, y la estructura del programa, insistimos no se ve afectada para nada. El resultado de utilizar este programa es, como podrá apreciarse, totalmente idéntico al obtenido en los ejercicios anteriores. El secreto de lo reducido de los cambios que han sido necesarios está en el uso de unas pocas funciones primitivas que hacen de puente entre las tareas del programa (altas, bajas, modificaciones) y las estructura empleadas para almacenar los datos. Los cambios de implementación afectan únicamente a los métodos de acceso (el "puente") y el resto del programa no se ve afectado. Dicho en términos orientados a objetos, el acceso indirecto a los atributos de una clase supone el aislamiento de la implementación interna, con notables ventajas si cambia esta última.
-
Ejercicio 0202r08.-
Transformar
struct Registro
en
struct NodoRegistro
, con vistas a crear una lista enlazada de registros. Analizar el consumo de memoria derivado del uso de una lista de punteros y de una lista enlazada.
La solución puede ser la siguiente:
struct NodoRegistro {
struct NodoRegistro * sig;
char nombre[15];
char ape_1[15];
char ape_2[15];
char direc[15];
char telef[9];
char DNI[8];
int edad;
float peso;
}
Notas
En cuanto al consumo de memoria, es interesante comprobar que una lista de punteros de
struct Nodo
y una lista enlazada formada por elementos del tipo
NodoRegistro
ocupan exactamente el mismo espacio si están llenas. En efecto, el consumo "basal" de la lista de punteros es mayor, porque es preciso reservar espacio -estático o dinámico- para la lista de punteros. Esto no ocurre en el caso de una lista enlazada, que efectivamente tiene un consumo nulo de memoria cuando está vacía. Cuando la lista de punteros está llena, esto es, cuando cada elemento de la lista señala un nodo de tipo
struct Registro
, se tiene el mismo consumo de memoria que si se emplearan nodos de tipo
struct NodoRegistro
para almacenar la misma información. La desventaja de una lista de punteros es el consumo "basal", y su ventaja es la rapidez de acceso al i-ésimo elemento. La desventaja de una lista enlazada es su relativa lentitud de acceso (forzosamente secuencial) a los elementos que la componen; su ventaja es doble: menor consumo en baja ocupación, y menos probabilidades de sufrir problemas por fragmentación del cúmulo. Téngase en cuenta que se puede rehacer la lista de punteros mediante
realloc()
para ampliarla, pero cuanto mayor sea, más difícil será encontrar un bloque libre del tamaño adecuado.