struct Nombre_de_tipo { tipo1 campo1 [,campo2,...]; ... tipon campom[,campop,...]; } ;Esta definición de nuevo tipo puede ir acompañada por una o más declaraciones de variables:
struct Nombre_de_tipo { tipo1 campo1 [,campo2,...]; ... tipon campom [,campop,...]; } variable1 [variable2,...] ;Por último, también es posible definir y declarar una estructura sin necesidad de indicar un nombre para el tipo de esa estructura:
struct { tipo1 campo1 [,campo2,...]; ... tipon campom[,campop,...]; } variable1[variable2,...] ;Evidentemente, no tiene sentido definir una estructura anónima y carente de nombre. Véase como ejemplo el siguiente Ejercicio .
struct Nombre_tipo_estructura { tipo_valido_bits nombre1 : longitud1; tipo_valido_bits nombre2 : longitud2; ... tipo_valido_bits nombreN : longitudN; } nombre_variable;En donde
tipo_valido_bits
denota uno de entre los tres tipos siguientes:
int
,
unsigned
o
signed
. Si la longitud es 1, el tipo tiene que ser obligatoriamente
unsigned
, porque un campo de 1 bit no puede tener signo (solo puede valer 0 o 1).struct { tipo1 campo1 [,campo2,...]; ... tipon campom[,campop,...]; } variable1[variable2,...] ;el código necesario para acceder al campoM de la
variableT
sería
variableT.campoM
en donde
variableT
denota la estructura y
campoM
denota el campo considerado. Los siguientes serían ejemplos válidos:
struct Ejemplo { char nombre[80]; int edad; }; /* Definición del nuevo tipo */ struct Ejemplo nombre_estructura; /* Declaración de una variable */ nombre_estructura.nombre = "José García"; nombre_estructura.edad = 34; printf("La edad de %s es de %d años.\n", nombre_estructura.nombre,nombre_estructura.edad);
struct Ejemplo { char nombre[80]; int edad; }; struct Ejemplo est_1, est_2; est_1.nombre = "José García"; est_1.edad = 34; est_2 = est_1; /* Se copia correctamente todos los campos. */Hay que tener especial cuidado con los campos que sean punteros.
#include<stdio.h> /* Construcción de Estructuras */ /* Ejemplo de ESTRUCTURA ANONIMA. Obsérvese la iniciación */ struct { char nombre[80]; int edad; float peso; } ficha_anonima = {"José Pérez", 33, 80.0 }; /* Ejemplo de DECLARACION DE UN TIPO STRUCT */ struct Fichas { char nombre[80]; int edad; float peso; }; struct Fichas otra_ficha = {"José Pérez", 33, 80.0 }; void main(void) { printf("Datos de la ficha de un tipo anónimo:\n\n"); printf("El nombre es %s\n",ficha_anonima.nombre); printf("La edad es %d\n",ficha_anonima.edad); printf("El peso es %f\n",ficha_anonima.peso); /* Esta asignación no está permitida, pese a que las estructuras son idénticas. otra_ficha = ficha_anonima; Tampoco se puede hacer una refundición de tipos: otra_ficha = (struct Fichas) ficha_anonima; */ printf("\n\nDatos de la ficha de un tipo conocido:\n\n"); printf("El nombre es %s\n",otra_ficha.nombre); printf("La edad es %d\n",otra_ficha.edad); printf("El peso es %f\n",otra_ficha.peso); printf("\n\nTerminación normal del programa.\n\n"); }
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.#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. */
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.#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. */
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.
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.#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. */
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>.
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. #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. */
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.
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.
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.
struct Registro bdd[MAX_PERSONAS]
anterior. El programa debe permitir la realización de altas, bajas y modificaciones.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"); } |
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.
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
).#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"); }
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.
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.
bdd
es un bloque formado por estructuras y reservado dinámicamente).#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"); }
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. 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; }
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.