Registro - Universidad de La Serena

Anuncio
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Registros
Los vectores como ya hemos visto permiten agrupar varios elementos de un mismo tipo. Cada
elemento de un vector es accesible a través de un índice, pero en ocasiones se necesitan
agrupar datos de diferentes tipos y/o preferir acceder a diferentes elementos de un grupo de
datos a través de un identificador, no de un índice. Un ejemplo de ello, es cuando Ud. completa
el llenado de un cheque, en donde incluye datos numéricos, caracteres, entre otros.
Los registros son agrupaciones heterogéneas de datos cuyos elementos (denominados campos)
son accesibles mediante identificadores. Veamos ahora un diseño típico de registro.
Supongamos que deseamos mantener los siguientes




datos de una persona:
su nombre (con un máximo de 40 caracteres),
su edad (un entero),
su RUT (una cadena de 9 caracteres).
Podemos definir un registro “persona” antes de la aparición de main:
#define MAXNOM 40
#define LONDNI 9
struct Persona {
char nombre[MAXNOM+1];
int edad;
char dni[LONDNI+1];
}; // <- Fijarse en el punto y coma: es fácil de olvidarse.
La definición de un registro introduce un nuevo tipo de datos en nuestro programa. En el
ejemplo hemos definido el tipo struct Persona (la palabra struct forma parte del nombre
del tipo). Ahora puedes declarar variables de tipo struct Persona así:
struct Persona pablo, robin, linda;
En tu programa puedes acceder a cada uno de los campos de una variable de tipo struct
separando con un punto el identificador de la variable del correspondiente identificador del
campo. Por ejemplo, pablo.edad es la edad de Pablo (un entero sin signo que ocupa un byte),
robin.nombre es el nombre de Robin (una cadena), y linda.rut[9] es la letra del RUT de
Linda. Cada variable de tipo struct Persona ocupa, en principio, 55 bytes: 41 por el nombre,
4 por la edad y 10 por el RUT. El porque se realizó este detalle de las variables, es para ilustrar
la función sizeof, se recuerdan, porque si es un tipo de dato cuál es su tamaño?. Recordemos
que el operador sizeof devuelve el tamaño en bytes de un tipo o variable. Analicemos el
siguiente programa:
#include <stdio.h>
struct Registro {
char a;
int b;
};
int main(void) {
printf ("Ocupacion: %d bytes\n", sizeof(struct Registro));
getch();
return 0;
}
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
1
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Parece que vaya a mostrar en pantalla el mensaje “Ocupación: 5 bytes”, pues como ya
sabemos un char ocupa 1 byte y un int ocupa 4. Pero no es así, porque nos entrega el mensaje:
Ocupación: 8 bytes
La razón de que ocupe más de lo previsto es la eficiencia. Las estaciones de trabajo o PC con
arquitectura de 32 bits agrupan la información en bloques de 4 bytes. Cada uno de esos bloques
se denomina “palabra”. Cada acceso a memoria permite traer al procesador los 4 bytes de una
palabra. Si un dato está a caballo entre dos palabras, requiere dos accesos a memoria, afectando
seriamente a la eficiencia del programa. El compilador trata entonces de generar un programa
eficiente y da prioridad a la velocidad de ejecución frente al consumo de memoria. En nuestro
caso, esta prioridad se ha traducido en que el segundo campo se almacene en una palabra
completa, aunque ello suponga desperdiciar 3 bytes en el primero de los campos. Esto era
solamente para justificar que la eficiencia vuelve a jugar un rol importante dentro de la
programación.
Un ejemplo: rectas de regresión para una serie de puntos en el plano
Hay métodos estadísticos que permiten obtener una recta que se ajusta de forma óptima a una
serie de puntos en el plano.
Si disponemos de una serie de n puntos (x1, y1), (x2, y2), . . . , (xn, yn), la recta de ajuste y =
mx + b que minimiza el cuadrado de la distancia vertical de todos los puntos a la recta se puede
obtener efectuando los siguientes cálculos:
Por favor, no se dejen impresionar, pues son sólo sumatorias. El programa que vamos a escribir
lee una serie de puntos (con un número máximo de, digamos, 1000), y muestra los valores de
m y b. Primero modelamos los puntos, a través de un registro:
struct Punto {
float x, y;
};
El vector de puntos, al que en principio denominaremos p, tendrá tamaño o talla de 1000:
#define TALLAMAX 1000
struct Punto p[TALLAMAX];
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
2
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Pero 1000 es el número máximo de puntos. El número de puntos disponibles efectivamente
será menor o igual y su valor deberá estar accesible en alguna variable. Olvidémonos del
vector p: nos conviene definir un registro en el que se almacenen vector y talla real del vector.
struct ListaPuntos {
struct Punto punto[TALLAMAX];
int talla;
};
Observe que estamos anidando structs. Necesitamos ahora una variable del tipo que hemos
definido:
#include <stdio.h>
#define TALLAMAX 1000
struct Punto {
float x, y;
};
struct ListaPuntos {
struct Punto punto[TALLAMAX];
int talla;
};
int main(void)
{
struct ListaPuntos lista ;
...
Reflexionemos brevemente sobre cómo podemos acceder a la información de la variable lista:
Expresión
Tipo y significado
lista
Es un valor de tipo struct ListaPuntos.
Contiene un vector de 1000 puntos y un
entero.
lista.talla
Es un entero. Indica cuántos elementos del
vector contienen información.
lista.punto
Es un vector de 1000 valores de tipo struct
Punto.
lista.punto[0]
Es el primer elemento del vector y es de tipo
struct Punto, así que está compuesto por dos
flotantes.
lista.punto[0].x
Es el campo x del primer elemento del
vector. Su tipo es float.
lista.punto[lista.talla-1].y
Es el campo y del último elemento con
información del vector. Su tipo es float.
lista.punto.x
¡Error! Si lista.puntos es un vector, no
podemos acceder al campo x.
lista.punto.x[0]
¡Error! Si lo anterior era incorrecto, esto lo
es aún más.
lista.punto.[0].x
¡Error! ¿Qué hace un punto antes del
operador de indexación?.
lista[0].punto
¡Error! La variable lista no es un vector, así
que no puedes aplicar el operador de
indexación sobre ella.
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
3
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Ahora que tenemos más claro cómo hemos modelado la información, vamos a resolver el
problema propuesto. Cada uno de las sumatorias se precalcula cuando se hayan leído los
puntos. De ese modo, simplificaremos significativamente las expresiones de cálculo de m y b.
Debe tener en cuenta que, aunque en las fórmulas se numeran los puntos empezando en 1, en C
se empieza en 0. Veamos el programa completo:
#include <stdio.h>
#define TALLAMAX 1000
struct Punto {
float x, y;
};
struct ListaPuntos {
struct Punto punto[TALLAMAX];
int talla;
};
int main(void)
{
struct ListaPuntos lista;
float sx, sy, sxy, sxx;
float m, b;
int i;
/* Lectura de puntos */
printf ("Puntos a leer: "); scanf ("%d", &lista.talla);
for (i=0; i<lista.talla; i++) {
printf ("Coordenada x del punto %d: ", i); scanf ("%f", &lista.punto[i].x);
printf ("Coordenada y del punto %d: ", i); scanf ("%f", &lista.punto[i].y);
}
/* Calculo de las sumatorias */
sx = 0.0;
for (i=0; i<lista.talla; i++)
sx += lista.punto[i].x;
sy = 0.0;
for (i=0; i<lista.talla; i++)
sy += lista.punto[i].y;
sxy = 0.0;
for (i=0; i<lista.talla; i++)
sxy += lista.punto[i].x * lista.punto[i].y;
sxx = 0.0;
for (i=0; i<lista.talla; i++)
sxx += lista.punto[i].x * lista.punto[i].x;
/* Calculo de m y b e impresion de resultados */
if (sx * sx - lista.talla * sxx == 0)
printf ("Indefinida\n");
else {
m = (sx * sy - lista.talla * sxy) / (sx * sx - lista.talla * sxx);
printf ("m = %f\n", m);
b = (sy * sxx - sx * sxy) / (lista.talla * sxx - sx * sx);
printf ("b = %f\n", b);
}
getch();
return 0;
}
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
4
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Otro ejemplo: Se recuerdan …gestión de una colección de Objetos, por ejemplo CDs.
Estamos en condiciones de abordar la implementación de un programa moderadamente
complejo, como es su proyecto: la gestión de una colección de CDs (aunque, todavía, sin poder
leer/escribir en archivo, pero eso luego viene, recuerde que Ud. deberá incluir este tema en su
proyecto final).
De cada CD almacenaremos los siguientes datos:




el título (una cadena con, a lo sumo, 80 caracteres),
el intérprete (una cadena con, a lo sumo, 40 caracteres),
la duración (en minutos y segundos),
el año de publicación.
Definiremos un registro para almacenar los datos de un CD y otro para representar la duración,
ya que se cuenta con dos valores (minutos y segundos):
#define LONTITULO 80
#define LONINTERPRETE 40
struct Tiempo {
int minutos;
int segundos;
};
struct CompactDisc {
char titulo[LONTITULO+1];
char interprete[LONINTERPRETE+1];
struct Tiempo duracion;
int anyo;
};
Al mismo tiempo, vamos a usar un vector para almacenar la colección, definiremos un máximo
número de CDs: Por ejemplo, 1000.
Eso no significa que la colección tenga 1000 discos, sino que puede tener a lo sumo 1000. ¿Y
cuántos tiene en cada instante? Utilizaremos una variable para mantener el número de CDs
presente en la colección. Mejor aún: definiremos un nuevo tipo de registro que represente a la
colección entera de CDs. El nuevo tipo contendrá dos campos:


el vector de discos (con capacidad limitada a 1000 unidades),
y el número de discos en el vector.
He aquí la definición de la estructura y la declaración de la colección de CDs:
#define MAXDISCOS 1000
...
struct Coleccion {
struct CompactDisc cd[MAXDISCOS];
int cantidad;
};
struct Coleccion mis_cds;
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
5
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
Nuestro programa, para que cumpla con las especificaciones debería permitir, al menos
efectuar las siguientes acciones:




Añadir un CD a la “base de datos”,
Listar toda la base de datos,
Listar los CDs de un intérprete,
Suprimir un CD dado su título y su intérprete… entre otras cosas..
El programa no resultará muy útil hasta que aprendamos a utilizar archivos en C, pues al
finalizar cada ejecución se pierde toda la información registrada.
Apuntemos ahora cómo enriquecer nuestro programa de gestión de una colección de discos
compactos almacenando, además, las canciones de cada disco. Empezaremos por definir un
nuevo registro: el que modela una canción. De cada canción nos interesa el título, el autor y la
duración:
struct Cancion {
char titulo[LONTITULO+1];
char autor[LONINTERPRETE+1];
struct Tiempo duracion;
};
Los registros son nuevos tipos de datos cuyo nombre viene precedido por la palabra struct. C
permite definir nuevos nombres para los tipos existentes con la palabra clave typedef. He aquí
un posible uso de typedef:
#define LONTITULO 80
#define LONINTERPRETE 40
struct Tiempo {
int minutos;
int segundos;
};
typedef struct Tiempo TipoTiempo;
struct Cancion {
char titulo[LONTITULO+1];
char autor[LONINTERPRETE+1];
TipoTiempo duracion;
};
typedef struct Cancion TipoCancion;
struct CompactDisc {
char titulo[LONTITULO+1];
char interprete[LONINTERPRETE+1];
TipoTiempo duracion;
int anyo;
TipoCancion cancion[MAXCANCIONES]; // Vector de canciones.
int canciones; // N´umero de canciones que realmente hay.
};
Sin embargo, existe otra forma más compacta aún de definir un nuevo tipo a partir de un
registro:
#define LONTITULO 80
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
6
Programación Estructurada( en C)
Registros
Dr. Eric Jeltsch F.
#define LONINTERPRETE 40
typedef struct {
int minutos;
int segundos;
} TipoTiempo;
typedef struct {
char titulo[LONTITULO+1];
char autor[LONINTERPRETE+1];
TipoTiempo duracion;
} TipoCancion;
typedef struct {
char titulo[LONTITULO+1];
char interprete[LONINTERPRETE+1];
TipoTiempo duracion;
int anyo;
TipoCancion cancion[MAXCANCIONES]; // Vector de canciones.
int canciones; // N´umero de canciones que realmente hay.
} TipoCompactDisc;
typedef struct {
TipoCompactDisc cd[MAXDISCOS];
int cds;
} TipoColeccion;
int main(void)
{
TipoColeccion mis_cds;
...
¿Cuál eligió Ud.?. Hasta la vista.
_________________________________________________________________________
Escuela Ingeniería en Computación, Universidad de La Serena.
7
Descargar