Estructuras 1 Objetivos Al finalizar esta actividad, serás capaz de: 1. Escribir la declaración de una estructura de datos convenientemente especificada 2. Escribir el código necesario para acceder a un elemento o conjunto de elementos de una estructura de datos 2 Motivación Hasta ahora hemos trabajado con datos elementales (enteros, reales, caracteres) o con vectores (agregaciones de datos del mismo tipo). En ocasiones es necesario trabajar con información que se compone de varios datos de tipos elementales diferentes. Por ejemplo, podemos tener una aplicación que trabaje con los clientes de nuestro banco. La información relativa a un cliente se podría componerse de: Identificador Un número entero Nombre Un vector de caracteres Saldo Un número real Y naturalmente, querremos trabajar con muchos clientes (quizá un vector de clientes). En el lenguaje de programación C, el mecanismo que permite trabajar con este tipo de estructuras de datos es la construcción struct (estructuras). En las próximas secciones veremos cómo trabajar con esta construcción. 3 Las estructuras en C Veamos primero un ejemplo de un programa que declara y usa estructura para representar a un cliente. #include <stdio.h> void main () { struct { int id; char nom[20]; float saldo; } cliente; //identificador del cliente // nombre printf ("Escribe el identificador y el nombre del cliente\n"); scanf ("%d %s", &cliente.id, cliente.nom); cliente.saldo =0.0; if (cliente.id > 100) printf (" El cliente es %s\n",cliente.nom); } 1 El programa declara una variable llamada cliente que es una estructura que contiene tres campos: un campo de tipo entero llamado id para almacenar el identificador del cliente, un vector de caracteres llamado nom para almacenar el nombre y un real llamado saldo para almacenar el saldo del cliente. El acceso a cada uno de esos campos es muy fácil. Simplemente se escribe el nombre de la variable, un punto y el nombre del campo al que queremos acceder. 1. Construye un proyecto que contenga el programa anterior, y verifica que funciona correctamente. Después haz la prueba siguiente: Pon un punto de parada en la sentencia if. Ejecuta el programa hasta ese punto de parada. Coloca la variable cliente en la ventana del watch. Esta ventana tendrá el aspecto mostrado a la derecha. El signo + dentro de un cuadrado indica que cliente es una variable compuesta y que, por tanto, contiene diversos campos con información. Haz clic en ese signo +. Se abrirá la estructura y la ventana de watch tendrá el aspecto de la derecha. La ventana te está mostrando los tres campos de la estructura, y el contenido de cada uno de esos campos. Fíjate que el campo nom es también una estructura compuesta (en este caso un vector). Podríamos abrirla para ver su contenido (haciendo clic en el signo +) pero no vale la pena porque ya estamos viendo que contiene la palabra Luis. 4 Vectores de estructuras Normalmente trabajaremos con vectores de estructuras. Fíjate en el ejemplo siguiente, en el que trabajamos con un vector de clientes. El programa lee del terminal la información de varios clientes, que guarda en un vector de clientes, y luego recorre el vector para detectar los clientes que tienen un saldo superior a 100.0 y escribe en la pantalla sus nombres. 2 #include <stdio.h> #define MAX 100 typedef struct { int id; char nom[20]; float saldo; } Tcliente; //identificador del cliente // nombre typedef struct { int numero; Tcliente cliente[MAX]; }Tlista; void main () { Tlista mis_clientes; int i; printf ("Escribe el numero de clientes\n"); scanf ("%d", &mis_clientes.numero); for (i=0; i<mis_clientes.numero; i++) { printf ("Escribe idenfificador, nombre y saldo\n"); scanf ("%d",&mis_clientes.cliente[i].id); scanf ("%s",mis_clientes.cliente[i].nom); scanf ("%f",&mis_clientes.cliente[i].saldo); }; for (i=0; i<mis_clientes.numero; i++) if (mis_clientes.cliente[i].saldo > 100.0) printf (" El cliente es %s\n",mis_clientes.cliente[i].nom); } Fíjate en la construcción typedef que aparece antes del programa principal. Con esta construcción estamos definiendo un nuevo tipo de dato que se llamará Tcliente, que es una estructura que contiene tres campos (identificador, nombre y saldo). En realidad todavía no hemos declarado ningún cliente. Simplemente estamos diciendo que a partir de este momento usaremos la palabra Tcliente para declarar variables que contendrán clientes. En el ejemplo se separa la definición de la estructura de la declaración de los variables. De este modo el programa queda más claro. Fíjate como hemos definido el tipo de estructura Tlista. En esta estructura tendremos la lista de clientes. La estructura contiene un campo para guardar el número de clientes de la lista, y otro campo que es en realidad un vector de clientes. Observa cómo hemos usado la palabra Tcliente para indicar que cada elemento del vector es en realidad una estructura que contiene la información de un cliente. En el vector caben hasta 100 clientes (el valor de la constante MAX) pero puede haber menos. En el campo numero tendremos siempre el número de clientes que realmente hay en el vector. 3 Una vista grafica de la estructura del tipo Tlista podría ser: mis_clientes numero id nom saldo cliente[0] id nom saldo --- ------ ------ ---- --- --- --- --- ---- --- --- ----- --- --- --- --- -- -- -- --- --- --- --- --- --- ----- --- --- --- --- --- --- --- --- --- --- --- --- --- cliente[1] id nom saldo cliente[MAX-1] Fíjate ahora en el programa principal, donde declaramos por fin una variable de tipo Tlista. Después leemos el número de clientes y entramos en un bucle para leer los datos de todos esos clientes. Finalmente, tenemos el bucle que recorre la lista para realizar la tarea. Fíjate finalmente cómo accedemos a los campos de las estructuras. Por ejemplo: mis_clientes.cliente[i].saldo Tal y como se ha explicado antes, primero se pone el nombre de la variable, y después un punto seguido el nombre del campo al que queremos acceder. En este ejemplo, como el campo es un vector, tendremos que poner el índice correspondiente a la posición del vector a la que queremos acceder. Como resulta que cada posición del vector es a su vez una estructura, tenemos que poner un punto seguido del nombre del campo de esa estructura al que queremos acceder. 2. Construye un proyecto que contenga la aplicación de ejemplo y verifica que funciona correctamente. Después has otra ejecución siguiendo las instrucciones siguientes. Pon un punto de parada al inicio del segundo bucle for y ejecuta hasta ese punto de parada (introduce el valor 3 como valor de numero de clientes) Coloca la variable mis_clientes en la ventana del watch. Clica sobre el signo + para abrir la variable. Veras una imagen como la que tienes a la derecha. Te indica que tienes 3 clientes, que están en el vector cliente. Haz clic en el signo + del campo cliente. Veras una imagen como la de la derecha. Estas viendo el vector de clientes. Haz clic en el signo + del cliente que hay en la posición 0. La imagen será como la de la derecha, y estarás viendo los datos del primer cliente. 4 5 Un ejemplo más complejo Ya sabes prácticamente toda la teoría que hay que saber para manejar estructuras y vectores de estructuras. Lo único que queda es algo más de práctica trabajando con estructuras complejas que contienen estructuras, que a su vez tienen dentro otras estructuras, etc. Para ello, veamos un ejemplo. Fíjate en las siguientes definiciones de estructuras: // Esta es la definición de la estructura de un piso typedef struct { int m2; /* metros cuadrados del piso */ int num_h; /* numero de habitaciones */ float precio; /* precio en euros */ } Tpiso; // Una planta es un vector de pisos typedef struct { int num_pisos; /* numero de pisos de la planta */ Tpiso piso [MAXPI]; } Tplanta; // Un edificio es un vector de plantas typedef struct { int num_plantas; /* numero de plantas del ediicio */ Tplanta planta [MAXPL]; } Tedificio; // Una calle es un vector de edificios typedef struct { int num_edificios; /* numero de edificios de la calle */ Tedificio edificio [MAXE]; } Tcalle; Supón que en el programa principal se han hecho las siguientes declaraciones: void main () { Tcalle mi_calle; Tpiso p; Veamos ahora algunos ejemplos de accesos a estas estructuras de datos. 1 Escribir en pantalla el precio del piso 2 de la planta 1 del edificio 4 printf (“%f\n”,mi_calle.edificio[4].planta[1].piso[2].precio); 2 Copiar en p los datos del piso 1 de la planta 0 del edificio 2 p= mi_calle.edificio[2].planta[0].piso[1]; 5 Fíjate que es posible copiar de golpe toda una estructura, en vez de copiar campo a campo. 3 Escribir en pantalla los metros cuadrados totales del edificio 3. m=0; for (i=0; i< mi_calle.edificio[3].num_plantas; i++) for (j=0; j< mi_calle.edificio[3].planta[i].num_pisos; j++) m = m + mi_calle.edificio[3].planta[i].piso[j].m2; printf (“Los metros totales del edificio 3 son %d\n”, m); 4 Escribir el precio del piso más caro de la calle. m = -1; for (i=0; i< mi_calle.num_edificios; i++) for (j=0; j< mi_calle.edificio[i].num_plantas; j++) for (k=0; k< mi_calle.edificio[i].planta[j].num_pisos; k++) if (m < mi_calle.edificio[i].planta[j].piso[k].precio) m =mi_calle.edificio[i].planta[j].piso[k].precio; printf (“El precio del piso más caro es %f\n”, m); 6 Ejercicios Ejercicio 1: Escribe la declaración de un tipo de estructura que se llamará talumno, con la siguiente información: Identificador de alumno: un entero Sexo: un carácter (‘V’ para varón y ‘H’ para hembra) Notas: un vector de 10 reales que contiene las notas del alumno en las 10 asignaturas Pasa: un booleano (cierto o falso) que nos dice si el alumno puede pasar o no de curso Escribe la declaración de una variable llamada alumno de tipo Talumno, y el código necesario para poner el campo pasa de esa variable a true si el alumno ha aprobado más de 6 asignaturas o a false en caso contrario. Ejercicio 2: Escribe la declaración en C de un vector de 50 elementos de tipo talumno. Escribe después el código necesario para subir, a todas las chicas, 0,5 puntos en la calificación de todas las asignaturas. Ejercicio 3: Supón que un colegio tiene como máximo 100 alumnos y cada alumno hace siempre 10 asignaturas. Escribe la declaración de la estructura de datos que se llamará Tcolegio, necesaria para guardar toda la información de un colegio. En concreto, la estructura debe tener: Un entero que será el identificador de colegio Un entero indicando el número de alumnos 6 Un vector de elementos de tipo talumno, con la información de cada alumno. Declara una variable de tipo Tcolegio y escribe el código necesario para actualizar el campo pasa de todos los alumnos del colegio (recuerda que el campo pasa debe ser true si el alumno ha aprobado más de 6 asignaturas y false en caso contrario) . 7