Está en la página 1de 8

6

ESTRUCTURAS DE DATOS (II):


REGISTROS

En el Capítulo 4 conocimos el concepto de array, una de las estructuras de datos estáticas más
habituales. Sin embargo, esta estructura tiene la limitación de permitir agrupar solamente datos de
un mismo tipo. En este capítulo trataremos otra estructura de datos estática, los registros, que nos
permitirán representar colecciones formadas por datos de distintos tipos.

6.1 REGISTROS
Los arrays nos permiten agrupar datos con la condición de que sean todos de un mismo tipo.
Existe un tipo de datos compuesto denominado registro que se puede utilizar para agrupar
información relacionada entre sí pero formada por datos de tipos distintos. Un tipo registro está
definido por un conjunto de componentes, denominados campos, que establecen su estructura. Cada
dato de tipo registro podrá tener ciertos valores asignados a sus campos.
Para trabajar con registros, es necesario en primer lugar definir la estructura de campos del tipo
registro. Para ello, definiremos un nuevo tipo con el que posteriormente podremos declarar variables
de dicho tipo.
En la fase de diseño, la definición de tipos se situará en una sección específica de la parte
declarativa, que titularemos TIPOS e irá situada inmediatamente después de la sección
CONSTANTES. En concreto, la definición de un tipo registro utilizará el siguiente diagrama
sintáctico:
identificador identificador
= : tipo
de tipo registro de campo
,

69
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

En lenguaje C, el tipo registro se representa con el tipo estructura, cuya definición se sitúa
inmediatamente después de la definición de constantes y obedece al siguiente diagrama sintáctico:

identificador
typedef struct { tipo ; }
de campo
,

identificador
;
de tipo registro

Por ejemplo, para representar la siguiente información de un alumno:


alumno
id 5
nombre Antonio González
grupo B
nota 6.125
puede definirse un tipo registro de la siguiente forma:
En algoritmia: En C:
TIPOS typedef struct
tipoAlumno = id:entero {
nombre[31]:carácter int id;
grupo:carácter char nombre[31];
char grupo;
nota:real
double nota;
} tipoAlumno;

Para utilizar un tipo registro, será necesario declarar variables de dicho tipo:
En algoritmia: En C:
VARIABLES tipoAlumno alumno;
alumno: tipoAlumno
Ahora bien, un registro contiene varios campos de información, por lo que para acceder a ellos
de forma independiente será necesario especificar el campo que nos interesa. En concreto, deberá
indicarse el identificador de la variable registro que lo contiene seguido de un punto (operador
denominado descriptor de campo) y del identificador de campo (por ejemplo, alumno.id,
alumno.nombre, alumno.grupo o alumno.nota). Un campo así especificado puede
tratarse como si fuera una variable del mismo tipo que el campo y, por tanto, se le podrán aplicar las
operaciones permitidas sobre variables de ese mismo tipo (por ejemplo, podrán realizarse
operaciones aritméticas sobre alumno.nota).
En C, el descriptor de campo es un operador con mayor prioridad que ningún otro (incluidos
todos los operadores unarios estudiados hasta el momento).
A diferencia de lo que ocurre con arrays, en C todos los valores de una variable registro pueden
asignarse a otra variable registro en una sola instrucción mediante el operador de asignación:
tipoAlumno alumno1,alumno2;
(...)
alumno2=alumno1;

70
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

También, a diferencia de los arrays, en C un registro puede pasarse por valor así como devolverse
asociado al nombre de la función. Por ejemplo, los siguientes prototipos de funciones son válidos:
tipoAlumno leerAlumno();
void mostrarAlumno(tipoAlumno alumno);
Por otro lado, al igual que en el tipo registro tipoAlumno uno de los campos era de tipo cadena,
es posible que otros campos sean de cualquier otro tipo de datos compuesto. Por ejemplo, podrían
representarse en el registro las calificaciones del alumno en 10 asignaturas distintas
alumno
id
nombre
grupo
notas
0123456789
utilizando un campo de tipo vector para las calificaciones, del siguiente modo:
typedef struct
{
int id;
char nombre[31];
char grupo;
double notas[10];
} tipoAlumno;
(...)
tipoAlumno alumno;
Para referirnos a la calificación del alumno en la tercera asignatura utilizaremos la sintaxis
alumno.notas[2].
Del mismo modo, a menudo es necesario representar en un programa, no un dato de tipo registro,
sino una colección de datos de tipo registro. Esto se puede implementar utilizando arrays cuyos
elementos son registros. Por ejemplo, lo más probable es que un programa requiera procesar la
información de un conjunto de alumnos y no de un solo alumno. Así, la información de los alumnos
matriculados en 3 titulaciones distintas, con 200 alumnos por titulación, puede representarse con la
siguiente matriz de registros:
0 1 199
id
nombre
... 0
grupo
notas
id
nombre
... 1
grupo
notas
id
nombre
... 2
grupo
notas
01234567890123456789 0123456789

71
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

En lenguaje C, la definición de esta estructura de datos sería:


typedef struct
{
int id;
char nombre[31];
char grupo;
double notas[10];
} tipoAlumno;
(...)
tipoAlumno alumnos[3][200];
Para referirnos al grupo del vigésimo tercer alumno de la segunda titulación emplearemos la
sintaxis alumnos[1][22].grupo, mientras que la sintaxis alumnos[0][33].notas[5]
haría referencia a la sexta nota de trigésimo cuarto alumno de la primera titulación.

EJEMPLO 6.1. Desarrollar un módulo que, recibiendo del módulo llamador un vector de registros de 'np'
poblaciones, con el nombre de cada población y su censo (número de habitantes en unidades de millar) durante 'nd'
décadas, muestre por pantalla, para cada población, su nombre y el número de orden de la década con mayor número
de habitantes (si hay varias décadas con el mayor número de habitantes, deberá mostrar el número de orden de la
primera de ellas). El número máximo de décadas es 10.
M. llamador

lPob,np,nd

mayorCenso
MÓDULO mayorCenso:
1º) ANÁLISIS:
a) Datos de entrada:
 np: Número de poblaciones. Módulo llamador (np > 0)
 nd: Número de décadas. Módulo llamador (nd > 0)
 lPob[1..np].nom: Nombre de cada población. Módulo llamador.
 lpob[1..np].hab[1..nd]: Miles de habitantes de cada población en cada década. Módulo llamador.
(lPob[i].hab[j] ≥0)
b) Datos de salida:
 lPob[1..np].nom: Monitor.
 decada[1..np]: Número de orden de la década con mayor censo de cada población. Monitor.
c) Comentarios:
 Se supone que en algún módulo ascendiente se ha definido lo siguiente:
CONSTANTES
MAXNOM=40
MAXDEC=10
TIPOS
tPob=nom[MAXNOM+1]:carácter
hab[MAXDEC]:reales
 Se utilizará una variable índice para recorrer la lista de poblaciones y otra para recorrer los censos de
cada población en busca del mayor censo.
 Se utilizará una variable para almacenar el índice de la década a la que pertenece el censo mayor de cada
población durante cada búsqueda.
2º) DISEÑO:
a) Parte declarativa:
mayorCenso(lPob[]:tPob, np:entero, nd:entero)
VARIABLES
ip,id,iMax:entero

72
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

b) Representación algorítmica:
mayorCenso(lPob,np,nd)
BLOCK

for do

ip ← 0, np-1, 1 BLOCK

iMax←0 for do escribir


(lPob[ip].nom,iMax+1)

id ← 1, nd-1, 1 if then

lPob[ip].hab[id] > iMax←id


lPob[ip].hab[iMax]

3º) CODIFICACIÓN:
#define MAXNOM 40 /* Tamaño máximo del nombre */
#define MAXDEC 10 /* Número máximo de décadas */

typedef struct
{
char nom[MAXNOM+1];
double hab[MAXDEC];
} tPob;

(...)

/** mayorCenso(lPob,np,nd) **/


/** Muestra, para cada población, su nombre y la década **/
/** con mayor censo. **/
void mayorCenso(tPob lPob[], int np, int nd)
{
int ip,id,iMax;

for (ip=0; ip<=np-1; ip=ip+1)


{
iMax=0;
for (id=1; id<=nd-1; id=id+1)
if (lPob[ip].hab[id] > lPob[ip].hab[iMax])
iMax=id;
printf("La población %s tiene su mayor censo en la década %d\n",
lPob[ip].nom,iMax+1);
}
}

73
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

EJERCICIOS

1º) El juego “Cricket simplificado” es un juego de dardos en el que solo se consideran los sectores
de la diana del 15 al 20. Un sector se considera cerrado cuando el mismo jugador acierta en ese
sector tres veces. Una vez que un jugador ha cerrado un sector, cada vez que vuelva a acertar en
ese sector, se le sumará al resto de jugadores que no lo hayan cerrado una puntuación igual a su
valor (por ejemplo, si un jugador ha cerrado el sector 15, cada vez que acierte sobre el 15 se le
sumarán 15 puntos al resto de jugadores que no lo hayan cerrado).
Desarrollar un módulo que procese y devuelva el efecto sobre el marcador de una tirada en
cualquier momento de la partida. Para ello, recibiendo la información referente al estado del
marcador antes de esa tirada (numero de jugadores y puntuación y estado de los sectores para
cada jugador) y la tirada a procesar (jugador que ha lanzado y sector acertado), deberá devolver
el nuevo contenido del marcador. Una tirada solo alterará el marcador cuando se acierte sobre
alguno de los números del 15 al 20.
2º) En el juego “Cricket simplificado” descrito en el ejercicio anterior, un jugador ganará la partida
cuando consiga cerrar todos los números y no tenga una puntuación mayor que otros jugadores.
Desarrollar un módulo que compruebe si algún jugador ha ganado o no la partida. Para ello,
recibiendo la información referente al estado del marcador (numero de jugadores y puntuación
y estado de los números para cada jugador), deberá devolver el ganador de la partida o el valor
0 si aún no hay ganador.
3º) En el juego de los barcos dos jugadores sitúan barcos de distintas longitudes (de 1 a 4 casillas)
en sendos tableros de 9×9 casillas. A continuación los jugadores realizan alternativamente
jugadas, consistente cada una en indicar una casilla (fila y columna) del tablero oponente y
cuyo resultado puede ser agua, tocado o hundido dependiendo de si la casilla no contiene
ninguna porción de barco, contiene una porción de un barco que dispone de otras porciones aún
sin destruir o contiene la última porción aún no destruida de un barco, respectivamente.
Para desarrollar este juego con un programa informático se empleará una representación de
tablero de tal forma que cada casilla represente la siguiente información: indicador de si la
casilla está o no ocupada por una porción de barco, tamaño del barco del que forma parte la
casilla, dirección en la que está situado el barco (horizontal o vertical), posición que ocupa la
porción respecto al barco completo (numeradas las porciones de izquierda a derecha para la
dirección horizontal y de arriba a abajo para la vertical) e indicador de si la porción está o no
destruida. Si la casilla no está ocupada por un barco, el resto de información se considerará
indeterminada (podrá contener cualquier valor pues el programa ignorará dicha información).
Desarrollar el módulo más adecuado que recibiendo del módulo llamador un tablero y una
jugada, devuelva el tablero actualizado y el resultado de la jugada (agua, tocado o hundido).
4º) Partiendo de la información sobre el juego de los barcos descrito en el ejercicio anterior,
desarrollar un módulo que permita añadir un barco en posición vertical a un tablero. Para ello,
recibiendo del módulo llamador el tablero actual, la posición (fila y columna) donde se situará
el extremo superior del barco y su longitud, devuelva la información del tablero actualizada. En
el desarrollo del módulo se supondrá que el barco no excederá de los límites del tablero durante
su colocación (se asumirá que todos las porciones del barco pueden situarse dentro del tablero).
El módulo devolverá también un indicador de si ha habido o no un error al intentar colocar
el barco (dependiendo de si las casillas donde se va a situar el barco están o no ocupadas). La

74
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

actualización del tablero solo debe llevarse a cabo en el caso de que no se dé tal error.
5º) En una fábrica se desea emplear un programa informático para evaluar el coste de fabricación
de sus productos. En concreto, la evaluación consiste en la suma de los costes de cada uno de
los componentes que constituyen cada producto. Para ello, se dispone de la lista de todos los
componentes disponibles en la fábrica, indicándose para cada uno de ellos el código del
componente (5 caracteres), nombre del componente (15 caracteres) y su coste por unidad en
euros. Además, cada producto se describe como una lista de componentes, indicándose para
cada componente su código y el número de unidades que integra dicho producto. Un producto
podrá contener, como máximo, 20 componentes distintos y el número de componentes distintos
disponibles en fábrica no podrá exceder los 500.
Desarrollar un módulo que, recibiendo del módulo llamador la lista de componentes
disponibles en la fábrica y su tamaño y la lista de componentes de un producto y su tamaño,
devuelva el coste total del producto.
6º) Desarrollar un módulo que, a partir de una jugada de ajedrez (fila y columna de la casilla de
origen y fila y columna de la casilla de destino) y el tablero actual recibidos del módulo
llamador, determine si un movimiento de torre es válido o no, devolviendo el resultado de la
comprobación. Para ello, deberá tenerse en cuenta lo siguiente:
a) Se asumirá que las coordenadas (filas y columnas) de ambas casillas que se reciben del
módulo llamador están dentro de los límites del tablero y apuntan a casillas distintas.
Además, se asumirá que las coordenadas de la casilla de origen apuntan a una torre.
b) Para representar cada casilla del tablero se utilizará un registro con dos campos que
representen el contenido de la casilla (P:peón, T:torre, C:caballo, A:alfil, D:dama, R:rey,
V:vacía) y el color de la pieza (B:blanca, N:negra). Si la casilla no contiene ninguna pieza,
la información relativa al color se considerará indeterminada (podrá contener cualquier valor
pues el programa ignorará dicha información).
c) Un movimiento de torre será válido si se desplaza por una fila o una columna, no hay piezas
entre las casillas origen y destino, y la casilla destino no está ocupada por una pieza de su
mismo color.
7º) Desarrollar un módulo que reciba una lista de valores enteros y su tamaño y devuelva un
registro con los siguientes 3 campos:
1. una lista sin repetición con los valores recibidos
2. una lista con el número de repeticiones de cada valor correspondiente de la lista anterior
3. el número de elementos en sendas listas
El tamaño máximo de la lista de valores recibido será 100.
8º) Se desea desarrollar un programa de cálculo de impuestos con las siguiente opciones de menú:
1. Añadir la información de cada unidad familiar
2. Listar la información de cada unidad familiar
3. Eliminar un miembro de una de las unidades familiares
4. Calcular y mostrar los impuestos de cada unidad familiar.
5. Salir del programa.
La información de cada unidad familiar consistirá en el apellido (10 caracteres), nombre (10
caracteres) e ingresos de su miembros, donde se supondrá que dos personas consecutivas con el
mismo apellido pertenecen a la misma unidad familiar.
El impuesto se calcula como sigue:
impuestos = porcentaje * ingresoAjustado
donde

75
CURSO 2019/20 6. ESTRUCTURAS DE DATOS (II): REGISTROS

{
ingresoAjustado
si ingresoAjustado < 30.000
porcentaje = 120.000
0.25 en otro caso
ingresoAjustado = ingresosTotales – (1200 * númeroDeducciones)
y el númeroDeducciones es igual al número de personas de la unidad familiar sin ingresos.
Cuando ingresoAjustado sea negativo, los impuestos serán 0 euros.
El proceso de entrada de datos finalizará cuando se indique como apellido XXXX. El listado
de impuestos consistirá en un listado en pantalla donde, en cada línea, deberá mostrarse el
apellido de una familia y su impuesto calculado. Como máximo, podrán tratarse 100 personas
en cada ejecución del programa. Por ejemplo, dada la entrada siguiente,
Apellidos: Castilla
Nombre: Rafael
Ingresos: 10150
Apellidos: Castilla
Nombre: María
Ingresos: 13820
Apellidos: Castilla
Nombre: Francisco
Ingresos: 0
Apellidos: Pérez
Nombre: Roberto
Ingresos: 39350
Apellidos: Pérez
Nombre: María
Ingresos: 7210
Apellidos: XXXX

el listado de impuestos sería:


FAMILIA IMPUESTOS
Castilla 4320.61
Pérez 11640.00

76

También podría gustarte