Fundamentos de Informática ETSID Tema 8: Estructuras. Departamento de Sistemas Informáticos y Computación Universidad Politécnica de Valencia Índice 1. Introducción. 2. Declaración del tipo estructura. 3. Declaración de variables del tipo estructura. 4. Tipos definidos por el usuario 5. Estructuras anidadas. 6. Vectores de estructuras. 7. Inicialización. 8. Acceso a los miembros de una estructura. 9. Operaciones sobre estructuras. 10. Estructuras como parámetros. 1 1. Introducción Una estructura es un tipo de dato compuesto que permite almacenar datos de diferente tipo (simples o estructurados). Se referencia con un único nombre. Ejemplo: con una misma variable se pueden guardar el nombre de una persona, su dni, su edad… Cuando se usa un tipo entero, el tipo ya está declarado, por lo que sólo hay que: 1. Declarar variables de tipo entero. Cuando se usa un tipo estructura, el tipo NO está declarado, por tanto se deben realizar dos pasos: 1. Declarar el tipo estructura. 2. Declarar variables del tipo estructura. 2 ¿En qué parte del programa se declaran las estructuras? 1. El tipo estructura se declara fuera del main y de las funciones para que sea global a todo el programa. 2. Las variables del tipo estructura se declaran en el programa principal (lo más habitual) o en la función que las utiliza (local). 2. Declaración del tipo estructura struct [nombre_estructura] { tipo_dato miembro_1; tipo_dato miembro_2; … tipo_dato miembro_n; } [lista de variables]; 3 z Miembro o campo: cada uno de los datos de la estructura (cada uno de ellos pertenece a un tipo de datos). z Identificador o nombre de la estructura: tipo de dato que se describe y del cual se podrán declarar variables. Es opcional (puede no declararse un tipo de dato y en su lugar declarar variables directamente). Ejemplo: struct persona { char nombre[80]; int edad, sexo; long int dni; long int telefono; }; Nombre del tipo: identificador (struct persona) Miembros 4 3. Declaración de variables de tipo estructura z z Implícita struct [nombre_estructura] { ... } variable1, variable2; Explícita struct nombre_estructura { … }; Se pueden combinar ambas formas de declarar variables. struct nombre_estructura variable1, variable2; struct { int a; float b; char c; } s1, s2, s3; struct coordenada { int a; float b; char c; }; struct coordenada { int a; float b; char c; } s1; struct coordenada s1, s2, s3; struct coordenada s2, s3; 5 Ejercicio Declarar una estructura de datos para contener los datos de una agenda personal: struct agenda { char nombre[81]; unsigned long int telefono_fijo; unsigned long int telefono_movil; char direccion[101]; }; 4. Tipos definidos por el usuario typedef tipo_dato nombre_tipo; typedef char caracter; caracter A, B, C; typedef struct { unsigned long int num_tarjeta; char tipo_cuenta; float saldo; } tipo_tarjeta; typedef permite cambiar de nombre un tipo de datos. tipo_tarjeta cliente; 6 typedef struct num_complejo { float real; Se puede nombrar este tipo float imaginaria; estructura de dos formas: } complejo; complejo o struct num_complejo complejo c1, c2, c3[800]; struct num_complejo c4, c5; typedef struct { float real; float imaginaria; } complejo; complejo c1, c2, c3[800]; struct num_complejo c4, c5; No se puede, porque no tiene nombre con struct 7 5. Estructuras anidadas struct fecha struct tarjetas { { int dia; long int num_tarjeta; int mes; char tipo_cuenta; char nombre[80]; int año; float saldo; }; struct fecha ultimo_pago; } cliente1, cliente2; num_tarjeta tipo_cuenta struct fecha { int dia; int mes; int año; }; nombre saldo ultimo_pago dia mes año Cuando una estructura está anidada en otra, la anidada debe estar escrita antes. struct tarjetas { long int num_tarjeta; char tipo_cuenta; char nombre[80]; float saldo; struct fecha ultimo_pago; } cliente1, cliente2; 8 6. Vectores de estructuras struct biblio { char titulo[80]; char autor[40]; float precio; }; struct biblio libreria[100]; titulo titulo autor autor precio precio libreria[0] libreria[1] titulo ... autor precio libreria[99] 7. Inicialización struct fecha { int dia; int mes; int año; }; struct tarjetas { long int num_tarjeta; char tipo_cuenta; char nombre[80]; float saldo; struct fecha ultimo_pago; }; struct fecha fecha1 = {1, 10, 2001}; struct tarjetas cliente1 = {34231, ‘A’, “Pedro García”, 3200.0, {12, 3, 1993}}; struct tarjetas clientes[100] = { 3250, ‘A’, “Pedro Sánchez”, 25000.56, {23, 3, 1997}, 3251, ‘C’, “Angel Molina”, 27025.36, {5, 24, 1997} }; 9 Ejemplo struct { char nombre[100]; unsigned int cod_ciudad; /* código */ int cod_provincia; unsigned int cod_postal; /* hasta 65535*/ unsigned long int num_habitantes; } poblacion = {“Valencia”, 35634, 46, 10056, 256789}; 8. Acceso a los miembros de una estructura variable_estructura.miembro [.miembro] struct fecha { cliente1.num_tarjeta = 44444; int dia; int mes; printf(“Nombre: %s\n”, cliente2.nombre); int año; }; cliente1.saldo = cliente1.saldo + 10000; struct tarjetas { long int num_tarjeta; cliente1.ultimo_pago.dia = 6; char tipo_cuenta; mis_clientes[25].ultimo_pago.mes = 12; char nombre[80]; float saldo; struct fecha ultimo_pago; } cliente1, cliente2, mis_clientes[100]; 10 9. Operaciones sobre estructuras z Lectura/escritura de estructuras: miembro a miembro. z Asignación de estructuras: directa, siempre que sean exactamente del mismo tipo. Ejemplo: struct cliente cliente1, cliente2; cliente1 = cliente2; 10. Estructuras como parámetros z Miembros de parámetro. una z Usar una retorno. z Paso de estructuras por referencia. estructura estructura como como valor de 11 Pasar como parámetro un miembro de la estructura float incremento (float saldo, int mes) { if (mes<=6) return 0.05*saldo; else return 0.0; } Incremento de saldo en un 5% para los pagos del primer void main() semestre { int i; struct tarjetas cliente[N]; for (i=0; i<N; i++) cliente[i].saldo += incremento (cliente[i].saldo, cliente[i].ultimo_pago.mes); } void incremento (float *saldo, float incremento) { *saldo = *saldo + incremento; } Incremento de saldo en la cantidad pasada como parámetro (el saldo se pasa por referencia) void main() { int i; struct tarjetas cliente[N]; for (i=0; i<N; i++) incremento (&(cliente[i].saldo), 300.6); } 12 Una estructura como valor de retorno struct tarjetas crear_tarjeta () { struct tarjetas cli; printf(“Número de tarjeta: ”); scanf(“%ld”, &cli.num_tarjeta); printf(“Tipo de tarjeta (A/C): ”); scanf(“%c”, &cli.tipo_cuenta); printf(“Nombre del cliente: ”); gets(cli.nombre); printf(“Saldo inicial de la tarjeta: ”); scanf(“%f”, &cli.saldo); return cli; } void main() { Llenar los datos de las tarjetas int i; struct tarjetas cliente[N]; for (i=0; i<N; i++) cliente[i] = crear_tarjeta(); } Otra versión struct tarjetas crear_tarjeta (struct tarjetas cli) { printf(“Número de tarjeta: ”); scanf(“%ld”, &cli.num_tarjeta); printf(“Tipo de tarjeta (A/C): ”); scanf(“%c”, &cli.tipo_cuenta); printf(“Nombre del cliente: ”); gets(cli.nombre); printf(“Saldo inicial de la tarjeta: ”); scanf(“%f”, &cli.saldo); return cli; } void main() { int i; struct tarjetas cliente[N]; for (i=0; i<N; i++) cliente[i] = crear_tarjeta(cliente[i]); } 13 Pase por referencia Cuando se pasa una estructura como un parámetro por referencia, para acceder a los miembros de la estructura dentro de la función, en lugar de usar un punto se usa una flecha (signo menos seguido de signo mayor). Acceso a los miembros de las estructuras en la función: punteroÆmiembro void modificar_tarjeta (tarjeta *cli) { printf(“Nuevo número de tarjeta: ”); printf(“Nuevo tipo de tarjeta (A/C): ”); printf(“Nuevo nombre del cliente: ”); printf(“Saldo inicial de la tarjeta: ”); } void main() { int i; tarjetas cliente[N]; for (i=0; i<N; i++) modificar_tarjeta(&cliente[i]); } scanf(“%ld”, &cliÆnum_tarjeta); scanf(“%c”, &cliÆtipo_cuenta); gets(cliÆnombre); scanf(“%f”, &cliÆsaldo); Modificar los datos de las tarjetas 14 Otra sintaxis void modificar_tarjeta (tarjeta *cli) { printf(“Nuevo número de tarjeta: ”); &(*cli).num_tarjeta); printf(“Nuevo tipo de tarjeta (A/C): ”); printf(“Nuevo nombre del cliente: ”); printf(“Saldo inicial de la tarjeta: ”); } scanf(“%ld”, scanf(“%c”, &*(cli).tipo_cuenta); gets((*cli).nombre); scanf(“%f”, (*cli).saldo); void main() { int i; tarjetas cliente[N]; for (i=0; i<N; i++) modificar_tarjeta(&cliente[i]); } Ejercicio La UPV está renovando su programa informático de gestión de alumnos. Para cada alumno desea almacenar sus datos personales y su expediente académico. Escribir un programa que obtenga los datos de los 35.000 alumnos de la UPV 15 Escribir dos funciones: z Lee: lee los datos de todos los alumnos de la upv (para cada alumno lee todas sus notas). z Escribe: escribe los datos de un único alumno de la universidad. #define N 35000 #define NUM_NOTAS 100 struct alumno { char nombre[80]; unsigned long int dni; float notas[NUM_NOTAS ]; }; 16 void lee (struct alumno a[N]) { int i, j; for (i=0; i<N; i++) { printf(“Nombre: “); gets(a[i].nombre); printf(“Dni: “); scanf(“%ld”, &a[i].dni); for (j=0; j<NUM_NOTAS; j++) { printf(“Nota de la asignatura %d: “); scanf(“%f”,&a[i].notas[j]); } } } void escribe (struct alumno al) { int j; printf(“Nombre: %s\n”, al.nombre); printf(“Dni: %ld”, al.dni); for (j=0; j<NUM_NOTAS; j++) printf(“Nota de la asignatura %d: %f”, j, al.notas[j]); } 17 void main () { struct alumno a[N]; int i; lee (a); for (i=0; i<N; i++) escribe (a[i]); } Ejercicio Una ONG decide diseñar un programa que le ayude a gestionar las distintas donaciones que recibe, dado que es una ONG de reciente creación, esta sólo tiene 10 proyectos de acción en curso. La información que guarda de cada proyecto de acción es la siguiente: z z z z Nombre de la persona responsable. Nombre del proyecto (cadena de caracteres). Cantidad presupuestada para realizar el proyecto. Cantidad recaudada por el momento para este proyecto (número real) y número de donaciones (entero). 18 Diseñar un programa en C que aporte las siguientes funciones (menú de opciones): z z z z Introducción de los datos iniciales: El programa solicitará los datos de los 10 proyectos iniciales. Donación de una cantidad: El programa solicitará el nombre del proyecto y la cantidad que se desea donar. Si todavía no se ha cubierto el presupuesto en su totalidad, se considerará la donación aunque esto provoque que se supere la cantidad presupuestada, en cuyo caso el proyecto dispone de un superhabit inicial. Si el presupuesto de ese proyecto ya había sido cubierto, se destinará este dinero al proyecto no cubierto con mayor diferencia entre las cantidades presupuestada y recaudada, y se avisará que la cantidad se ha destinado a otro proyecto indicando el nombre. Listado de los proyectos no cubiertos: Mostrará por pantalla los datos de los proyectos cuyo presupuesto no ha sido cubierto todavía. Salir: Escogida esta opción el programa finalizará. #include <stdio.h> #include <string.h> #define TRUE 1 #define FALSE 0 #define N 10 /* Número de proyectos */ struct proyecto { char nombre_responsable[50]; char nombre_proyecto[50]; float presupuesto; float recaudado; int numero_donaciones; }; El tipo se declara como global para que puedan utilizarlo todas las funciones 19 /* Menú general */ int menu () { int c; do { printf("1. Introducir datos iniciales\n"); printf("2. Donación\n"); printf("3. Listado de proyectos no cubiertos\n"); printf("4. Salir\n"); printf("Opción: "); scanf("%d",&c); if ((c<1) || (c>4)) printf("Opción Incorrecta\n"); } while ((c<1) || (c>4)); return c; } /* Introducción de los datos iniciales de los proyectos */ void introducir_datos (struct proyecto proyectos[N]) { int i; for (i=0; i<N; i++) { printf("Proyecto %d\n", i+1); printf("Responsable: "); gets(proyectos[i].nombre_responsable); printf("Proyecto: "); gets(proyectos[i].nombre_proyecto); printf("Presupuesto: "); scanf("%f", &proyectos[i].presupuesto); printf("Recaudado hasta el momento: "); scanf("%f", &proyectos[i].recaudado); printf("Número de donaciones: "); scanf("%d", &proyectos[i].numero_donaciones); } } 20 /* Listado de los proyectos cuyo presupuesto no ha sido cubierto todavía */ void listado (struct proyecto proyectos[N]) { int i; for (i=0; i<N; i++) if (proyectos[i].presupuesto > proyectos[i].recaudado) { printf("Proyecto %d\n", i+1); printf("Responsable: %s\n", proyectos[i].nombre_responsable); printf(“Nombre del proyecto: %s\n", proyectos[i].nombre_proyecto); printf("Presupuesto: %.2f\n", proyectos[i].presupuesto); printf("Recaudado hasta el momento: %.2f\n", proyectos[i].recaudado); printf("Número de donaciones: %d\n", proyectos[i].numero_donaciones); } } /* Donación de una cantidad */ void donacion (struct proyecto proyectos[N]) { char nombre_proyecto[50]; float donacion, diferencia, mayor_diferencia=0; int i=0, j, encontrado=FALSE, nuevo; printf("Nombre del proyecto: "); gets(nombre_proyecto); while ((i<N) && encontrado==FALSE) if (strcmp(proyectos[i].nombre_proyecto, nombre_proyecto) == 0) encontrado = TRUE; else i++; 21 if (encontrado==TRUE) { printf("Cantidad a donar: "); scanf("%f", &donacion); if (proyectos[i].recaudado<proyectos[i].presupuesto) { /* Se añade al proyecto la cantidad donada */ proyectos[i].recaudado += donacion; proyectos[i].numero_donaciones++; } else { /* La donación se destina a otro proyecto */ nuevo=-1; for (j=0; j<N; j++) { /* Hayar el máximo de la diferencia */ diferencia = proyectos[j].presupuesto - proyectos[j].recaudado; if ((diferencia>mayor_diferencia) && (diferencia>0)) { mayor_diferencia = diferencia; nuevo = j; } } /* Nuevo es el proyecto con mayor diferencia entre recaudado y presupuestado. Si nuevo es –1 significa que no hay ningún proyecto que cumpla las condiciones y no se hará la donación */ if (nuevo>0) { printf("La donación se destina al proyecto %s\n", proyectos[nuevo].nombre_proyecto); proyectos[nuevo].recaudado += donacion; proyectos[nuevo].numero_donaciones++; } else printf("No es posible hacer donaciones\n"); } } else /* No se ha encontrado un proyecto con el nombre indicado */ printf("Proyecto no existe\n"); } 22 void main() /* Programa Principal */ { struct proyecto proyectos[N]; int opc; do { opc = menu (); switch(opc) { case 1: introducir_datos (proyectos); break; case 2: donacion (proyectos); break; case 3: listado (proyectos); break; } } while (opc != 4); } Ejercicio En la secretaría general de la UPV se han realizado las preinscripciones para cada uno de los centros (escuelas y facultades). Para realizar la preinscripción la secretaría rellena los siguientes datos para cada uno de los alumnos: z z z Nombre. Dni. Código del centro al que se preinscribe. El centro al que se inscribe está codificado (1-EUITI, 2-ETSII,...). La secretaría envía los datos almacenados de las preinscripciones a cada uno de los centros (los mismos datos a todos los centros) para obtener los datos de los alumnos matriculados. Los centros seleccionan los datos de los alumnos preinscritos a su centro, añadiendo además el nombre de la titulación y el de la especialidad a la que están preinscritos. Estos datos se rellenan a partir de los sobres de matrícula que entregan los alumnos. 23 Implementar un programa que solicite los datos de la preinscripción. Después debe solicitar el código del centro del que se desean seleccionar los datos y tome los datos de la preinscripción a dicho centro, rellene los datos que faltan y saque un listado con nombre, dni, titulación y especialidad de cada alumno preinscrito. El número máximo de alumnos preinscribirse en la UPV es de 50.000. que pueden #include <stdio.h> #include <string.h> #define ALUMNOS 50000 /* Tipo de datos de un alumno preinscrito */ typedef struct { char nombre[50]; char dni[10]; int centro; } alumno_preinscrito; /* Datos de un alumno matriculado en un centro en concreto */ typedef struct { char nombre[50]; char dni[10]; char titulacion[50]; char especialidad[50]; } alumno_matriculado; 24 /* Solicita los datos de la preinscripción: solicita al usuario los datos para rellenar el vector de alumnos preinscritos en la UPV. */ void rellena_preinscripcion (alumno_preinscrito p[ ]) { int i; for (i=0; i<ALUMNOS; i++) { printf("Nombre del alumno: "); gets(p[i].nombre); printf("DNI: "); scanf("%s",p[i].dni); printf("Código del centro: "); scanf("%d",&p[i].centro); } } /* Obtiene los datos del centro */ int obten_datos (alumno_preinscrito p[ ], alumno_matriculado m[ ], int codigo) { int i, j=0; for (i=0; i<ALUMNOS; i++) if (p[i].centro==codigo) { /* Copia los datos de la preinscripción en la matrícula */ strcpy(m[j].nombre, p[i].nombre); strcpy(m[j].dni, p[i].dni); /* Solicita los datos que faltan */ printf("Titulación: "); gets(m[j].titulacion); printf("Especialidad: "); gets(m[j].especialidad); j++; } return j; /* Nº de alumnos matriculados para el listado */ } 25 /* Listado */ void listado(alumno_matriculado m[ ], int n, int codigo) { int i; printf("Listado de alumnos del centro %d\n", codigo); for (i=0; i<n; i++) printf("%s %s %s %s\n",m[i].nombre, m[i].dni, m[i].titulacion, m[i].especialidad); } void main() { alumno_preinscrito preinscripcion[ALUMNOS]; alumno_matriculado matricula_centro[ALUMNOS]; int codigo, matriculados; /* Solicita los datos de la preinscripción */ rellena_preinscripcion(preinscripcion); /* Obtiene los datos del centro */ printf("Introduzca el código del centro: "); scanf("%d",&codigo); matriculados = obten_datos (preinscripcion, matricula_centro, codigo); /* Listado */ listado(matricula_centro, matriculados, codigo); } 26