Estructura de Computadores 2ITIEI Estructura de Computadores Cesáreo Fernández Martínez Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 1 Estructura de Computadores 2ITIEI Contenidos • La asignatura tiene básicamente dos bloques – Estudio de la estructura de los computadores • CPU, memoria, etc. – Estudio de la técnicas de programación avanzadas en C • Estructuras, Listas, Árboles, etc Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 2 1 Estructura de Computadores 2ITIEI Programa I • Estudio de la técnicas de programación avanzadas en C – Punteros • Punteros y vectores • Punteros y funciones – Estructuras de datos dinámicas • Introducción • Elementos de una estructura dinámica de datos: Nodo • Tipos de estructuras dinámicas de datos – Listas enlazadas – Árboles binarios – Algoritmos de ordenación. • • • • • Introducción Ordenación de vectores numéricos Método de la burbuja (o intercambio directo) Método rápido (quicksort). Ordenación de vectores de cadenas Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 3 Estructura de Computadores 2ITIEI Programa II • Arquitectura de computadores – La CPU: ALU, Registros, CU – La memoria – El Conjunto de Instrucciones (Instruction Set Arquitecture: ISA) • Organización de una máquina RISC – Reduced Instruction Set Computer, ó – Ridiculously Simple Computer . Veremos: • El conjunto de instrucciones que puede ejecutar (Arquitectura) • Organización – Los módulos que lo forman: ALU, Registros, etc. – Cómo están conectados (organización) – Cómo ejecuta instrucciones Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 4 2 Estructura de Computadores 2ITIEI Resumen de Normas • (Consultar Web de la universidad) • La asignatura consta de: – Teoría – Laboratorio • Teoría: Dos exámenes – Intermedio: Técnicas avanzadas de programación – Final: Programación más máquina RISC – Pruebitas intermedias: Según necesidades de guión • Laboratorio – Sesiones de 2 horas – Importancia del trabajo previo • Test de 10 min al comienzo de la práctica Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 5 Estructura de Computadores 2ITIEI Evaluación • Nota final – Nota = nota_teoría * 0.6 + nota_lab * 0.4; – Si ambas notas son mayores de 5.0 – Caso contrario se selecciona la menor de las notas • En forma de pseudocódigo C if (nota_teoría>5 && nota_lab>5) { nota = nota_teoría*0.6 + nota_lab*0.4; } else { if (nota_teoría < nota_lab) { nota = nota_teoría; } else { nota = nota_lab; } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 6 3 Estructura de Computadores 2ITIEI Nota de teoría • Pruebas – Prueba final – Prueba intercuatrimestral – La pruebas intercuatrimestral sólo sirve para subir nota; en ese caso contará un 25% de la nota de teoría. • En forma de pseudocódigo C nota_c = nota_examen_final*0.75 + nota_intercuatrimestral*0.25; if (nota_c > nota_examen_final) { nota_teoría = nota_c; } else { nota_teoria = nota_examen_final; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 7 Estructura de Computadores 2ITIEI Nota de laboratorio • Prácticas en • Calificación basada en – http://www.iit.upco.es/~cesar – Tests previos a la práctica • Basados en el trabajo previo de la práctica – Dos exámenes • Intercuatrimestral y final – Informes de la práctica. • Se entregan la semana siguiente a la práctica • No informes => no examen • En forma de pseudocódigo C nota_lab = exam_1*0.25+exam_2*0.50+ test*0.25; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 8 4 Estructura de Computadores 2ITIEI Bibliografía • [Muñoz y Palacios 2006] José Daniel Muñoz Frías y Rafael Palacios Hielscher. Fundamentos de programación utilizando el lenguaje C. – R.B. Servicios Editoriales. S.L. • Apuntes de arquitectura de ordenadores. Página WEB – http://www.iit.upco.es/~cesar Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 9 Estructura de Computadores 2ITIEI Bibliografía adicional • [Antonakos and Mansfield, 1997] Antonakos, J. L. and Mansfield, K. C. (1997). Programación estructurada en C. • [Wirth, 1986] Wirth, N. (1986). Algoritmos + estructuras de datos = programas. • [Ibáñez, 1995] Ibáñez, J. B. (1995). Introducción a la arquitectura de computadores. – Prentice Hall Iberia, Madrid. – Ediciones del Castillo S. A. – Universidad de Valladolid, Valladolid. • [Martínez et al., 2001] Martínez, R. J., Boluda, J. A., and Pérez, J. J. (2001). Estructura de computadores y periféricos. – Editorial Ra-Ma, Madrid. Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 10 5 Estructura de Computadores 2ITIEI Revisión de C Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 11 Estructura de Computadores 2ITIEI Punteros • Almacenan la dirección de una variable – La dirección de memoria en la que está almacenada la variable • Parte importante del lenguaje C. Se usan para – Definir vectores de tamaño variable – En funciones que devuelven varias cosas – En estructuras de datos complejas • Listas • Árboles • etc Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 12 6 Estructura de Computadores 2ITIEI Gráficamente • El puntero es una VARIABLE (en memoria) que almacena la DIRECCIÓN de otra VARIABLE (apunta a ...) Puntero Dirección Contenido Char * C2B8 A7 int * C37A B9 A4 long * 2703 E2 A1 10 30 • Las direcciones de memoria se dan en Hexacimal – C2B8 , C37A, etc – Todas las direcciones (y por tanto los punteros) del MISMO TAMAÑO Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 13 Estructura de Computadores 2ITIEI Otra respresentación gráfica • Dirección/contenido(valor) Dirección Valor Variable ? C2B8 pc C2B8 A7 c ? C37A pl C37A C37B C37C C37D E2 A1 10 30 l • Dirección El puntero guarda la dirección Valor Variable – Del primer elemento (byte) • del objeto apuntado Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 14 7 Estructura de Computadores 2ITIEI Punteros en C • Se declaran mediante el operador * int i; int *pi; • // declara una variable entera // declara un puntero a variable entera Se inicializa con & i = 78; pi = &i ; • // la variable i almacena un 78 // pi apunta a i Se accede al valor (de la variable apuntada) con * printf(“/d\n”, *pi); // imprime el valor de i Cesáreo Fernández Martínez (78) UPCO- ICAI Departamento de Electrónica y Automática 15 Estructura de Computadores 2ITIEI Operaciones con punteros • Operando con la variable apuntada int i; int *pi; i = 78; pi = &i; • *pi = *pi + 8; *pi += 8; // suma 8 a i // suma 8 a i pi += 8; // suma 8 al puntero // el puntero apunta 8 int’s más allá PELIGRO !!! – Sólo tiene sentido incrementar el puntero cuando se trabaja con vectores Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 16 8 Estructura de Computadores 2ITIEI Punteros y vectores • Vectores – Todos los elementos de igual tamaño – Almacenados en posiciones consecutivas de memoria • Basta conocer la dirección del primer elemento y el tamaño del vector int main(void) { double a[N]; int i; double *pd; for (i= 0; i<N; i++) { a[i] = 7.8 * i; } pd = &a[0]; for (i= 0; i<N; ++) { printf(“%f\n”, *pd); pd++; } return 0; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 17 Estructura de Computadores 2ITIEI Funciones • Las funciones son un elemento básico para la estructuración de los programas – Hacer funciones cortas y que hagan una sola cosa ! (fáciles de desarrollar y de depurar, reutilizables) • • Las funciones deben ser declaradas y definidas Declaración (prototipo) int power(int base, int n); • Definición (código) int power(int base, int n) { int i, p; p = 1; for (i=1; i<=n; i++) p = p * base; return p; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 18 9 Estructura de Computadores 2ITIEI Prototipo • A incluir en la cabecera de programa cuando las funciones se definen en el fichero de entrada DESPUES de main. int power(int base, int n); int main (void) { printf(”%d %d \n”, 10, power(2,10)); } int power(int base, int n) { int i, p; p = 1; for (i=1; i<=n; i++) p = p * base; return p; } • El prototipo indica – Número y tipo de argumentos (de la función) – Tipo de resultado Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 19 Estructura de Computadores 2ITIEI Punteros y funciones • Los parámetros se puede pasar por – Valor: Tipos básicos (atómicos: char, int, long, etc.). – Referencia: Tipos compuestos (vectores, cadenas). • Paso por referencia para tipos atómicos. Ejemplo: Por Valor void cambio(int a, int b) { int temp; temp = a; a = b; b = temp; } int main (void) { int x=5, y=6; cambio(x,y); } Cesáreo Fernández Martínez Por Referencia void cambio(int *pa, int *pb) { int temp; temp = *pa; *pa = *pb; *pb = temp; } int main (void) { int x=5, y=6; cambio(&x,&y); } UPCO- ICAI Departamento de Electrónica y Automática 20 10 Estructura de Computadores 2ITIEI Devolver varios valores • Devolver mediante return • Mediante la lista de argumentos: paso por referencia – Un solo valor. – Tantos como se desee void SumaProd(double *, double *, double, double) ; int main (void) { double sum; couble prod; SumaProd(&sum, &prod, 2.0, 3.0); printf(”%f %f \n”, sum, prod); } void SumaProd(double *psuma, double *pprod, double dato1, double dato2) { *psuma = dato1 + dato2 *pprod = dato1 * dato2 } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 21 Estructura de Computadores 2ITIEI Vectores con [ ] • El nombre del vector es un puntero (constante) – a[3] = 2.7; • Es idéntico a – *(a+3) = 2.7; • Además: cualquier puntero se puede usar como vector! – *punt = 4; • Es idéntico a – punt[0] = 4; • Y – *(punt+3) = 4; es idéntico a punt[3] = 4; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 22 11 Estructura de Computadores 2ITIEI Funciones, vectores y punteros • Copia de cadenas con índices void strcpy(char cadd[],char cads[] ) { int i=0; while (cads[i] != ’\0’) { cadd[i] = cads[i]; i++; } cadd[i] = cads[i]; // 0 del final } int main (void) { char s1[] = ”Hola”; char s2[25]; // espacio suficiente strcpy(s2,s1); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 23 Estructura de Computadores 2ITIEI Funciones, vectores y punteros II • Con punteros void strcpy(char *pd,char *ps) { while (*ps != ’\0’) { *pd = *ps; pd++; ps++; } *pd = *ps; // 0 del final } int main (void) { char s1[] = ”Hola”; char s2[25]; strcpy(s2,s1); } Cesáreo Fernández Martínez // cadena // cadena UPCO- ICAI Departamento de Electrónica y Automática 24 12 Estructura de Computadores 2ITIEI Funciones, vectores y punteros III • Ambas declaraciones de punteros son equivalentes – char s[ ] y char *ps • Lo que se pasa a la función cuando hacemos – strcpy(s2,s1); • • Son las DIRECCIONES de s2 y s1 strcpy carga ambas direcciones en sendos punteros – Maneja las cadenas de main usando los punteros ! • RESERVAR siempre suficiente ESPACIO en la CADENA DESTINO !!!!! – El puntero no es más que “un mecanismo alternativo” de acceso a la cadena. – pd[25] está fuera de la cadena (escribe en una variable DISTINTA de la cadena apuntada por pd !) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 25 Estructura de Computadores 2ITIEI Memoria dinámica • Idónea cuando no se conocen las dimensiones “a priori” – Sólo se conoce al ejecutar el programa – Se le pide al usuario a principio de la ejecución • Llamadas a sistema operativo para – Reservar memoria • El operativo devuelve un puntero • Liberar la memoria utilizada • • Cuando deja de necesitarse Funciones calloc y malloc (en stdlib.h) void *calloc(size_t numero_elementos, size_t tamaño_elemento) ; void *malloc(size_t tamaño_bloque) ; • Devuelven – Puntero a la base del espacio de memoria reservado – Puntero NULL sin no queda memoria suficiente (Comprobar) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 26 13 Estructura de Computadores 2ITIEI Memoria dinámica II • La función calloc – Reserva un bloque de memoria para • Un número de elementos del tamaño indicado • La función malloc – Reserva un bloque de memoria de • Tamaño indicado en BYTES • Ej: para crear un vector de 100 enteros int *pent; pent = (int *) calloc(100, sizeof(int)); if (pent == NULL) { printf(“Error: No hay suficiente memoria\n”); } else { pent[0] = 27; .. free(pent); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 27 Estructura de Computadores 2ITIEI Función free • Se utiliza para liberar la memoria reservada con malloc/calloc void free(void *puntero_al_bloque) ; • El puntero debe apuntar al principio del bloque ! – El siguiente código da un error ! int *pent; pent = (int *) calloc(100, sizeof(int)); if (pent == NULL) { printf(“Error: No hay suficiente memoria\n”); } else { .. pent++; .. free(pent); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 28 14 Estructura de Computadores 2ITIEI Algoritmo-pseudocódigo • Queremos resolver el siguiente problema – Pedir al usuario un vector de dimensión variable, y – Generar otro vector con los números pares del vector anterior • Detallamos el proceso a seguir en forma de pseudocódigo - • Pedir dimensión del vector a usuario Reservar memoria para el vector Pedir al usuario los elementos de vector Contar el número de elementos pares del vector Reservar memoria para el vector de números pares Copiar los números pares desde el primer vector al segundo Imprimir el segundo vector Liberar memoria Ejemplo 1 – Vector de números pares Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 29 Estructura de Computadores 2ITIEI Recomendaciones/advertencias • Cuando se usa memoria estática: – Definir los vectores en el programa principal (dimensión fija) – Pasar las direcciones de comienzo a las funciones – Las funciones no pueden devolver vectores !!! • Cuando se usa memoria dinámica: – El programa principal declara punteros – La memoria se reserva mediante malloc/calloc – Estas funciones devuelven punteros, que se almacenan en los punteros previamente declarados – Accedemos a las posiciones de memoria mediante * y/o [ ] Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 30 15 Estructura de Computadores 2ITIEI Estructuras de datos • Ideales para agrupar datos de distinto tipo – Referentes a un mismo objeto • Pe.: Para describir el historial médico de una persona Dato Tipo de dato Tipo en C Nombre Edad Peso Nº Seguridad Social Historia Cadena de caracteres Número entero Número en punto flotante Número entero Cadena de caracteres char[100] unsigned short float unsigned long char[1000] Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 31 Estructura de Computadores 2ITIEI Declaración y definición de estructuras • Declaración struct nombre_de_estructura { tipo_1 nombre_miembro_1; tipo_2 nombre_miembro_2; . . . . tipo_n nombre_miembro_n; } • • La declaración no crea objetos, introduce un nuevo nombre Para crear un objeto (definición): struct nombre_de_estructura • var; Crea la variable var del tipo nombre_de_estructura Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 32 16 Estructura de Computadores 2ITIEI Ejemplo • Historial médico de un paciente struct paciente { char unsigned short float unsigned long char }; • nombre[100]; edad; peso; n_seg_social; historia[1000]; Para crear objetos de este tipo: struct paciente pepito; struct paciente juanito; • Los objetos de tipo estructura se crean de la misma forma que los objetos de tipo básico. Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 33 Estructura de Computadores 2ITIEI Sentencia typedef • Permite asociar un nombre cualquiera a un tipo – Basico – Derivado typedef unsigned short WORD; typedef unsigned char U8; • Para definir objetos de estos nuevos tipos WORD edad; U8 caracter; • // edad es un entero corto: 16 bits // caracter almacena un byte sin signo: 8 bits Ventajas de legibilidad y facilidad a la hora de portar el programa a otros ordenadores Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 34 17 Estructura de Computadores 2ITIEI Estructuras y typedef • Declaración con typedef typedef struct paciente { char nombre[100]; unsigned short edad; float peso; unsigned long n_seg_social; char historia[1000]; }PACIENTE; • Para crear objetos de este tipo: PACIENTE pepito; PACIENTE juanito; • Lo que hace el programa más legible Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 35 Estructura de Computadores 2ITIEI Estructuras dentro de estructuras • Una nueva estructura typedef struct dia { int dia; int mes; int año; } FECHA; • permite introducir la fecha de nacimiento del paciente en la estructura PACIENTE typedef struct paciente { char nombre[100]; FECHA fecha_de_nacimiento; float peso; unsigned long n_seg_social; char historia[1000]; } PACIENTE; • de forma fácil Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 36 18 Estructura de Computadores 2ITIEI Acceso a los campos de una estructura • Operador: ‘.’ – Se usa para acceder a los campos de una estructura. Ej: pepito.edad = 27; pepito.peso = 67.5 strcpy(pepito.nombre, “José Pérez López”); • Para imprimir los datos printf(“Paciente: %s\n”, pepito.nombre); printf(“Edad: %d\n”, pepito.edad); printf(“Peso: %f\n”, pepito.peso); printf(“Num. Seg. Social: %ld\n”, pepito.n_seg_social); printf(“Historia: %s\n”, pepito.historia); Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 37 Estructura de Computadores 2ITIEI Estructuras y funciones • Operador: ‘=‘, se puede usar para copiar estructuras de mismo tipo PACIENTE pepito; PACIENTE copia_de_pepito; copia_de_pepito = pepito; – Al contrario de lo que sucede con los vectores • Que se debe realizar componente a componente • Las estructuras se pueden pasar directamente a las funciones – Ya que en el paso se copia la estructura en el argumento de la funcíon (paso por valor) • Una función puede devolver una estructura vía return ! Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 38 19 Estructura de Computadores 2ITIEI Ejemplo II. Números Complejos • Declaración typedef struct complejo{ double real; double imag; } COMPLEJO; • Para crear variables: int main(void) { COMPLEJO comp1, comp2, result; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 39 Estructura de Computadores 2ITIEI Ejemplo • La siguiente función devuelve una estructura tipo complejo COMPLEJO InicComplejo(double real, double imag) { COMPLEJO resultado; resultado.real = real; resultado.imag = imag; return resultado; } • Para llamar a la función (inicializa el número complejo): . . . COMPLEJO comp1; . . . Comp1 = InicComplejo(2.0, 3.7); . . . Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 40 20 Estructura de Computadores 2ITIEI Paso de parámetros • El paso de parámetros es similar COMPLEJO SumaComp(COMPLEJO c1, COMPLEJO c2) { COMPLEJO res; res.real = c1.real + c2.real; res.imag = c1.imag + c2.imag; return res; } • Para llamar a la función: . . . suma = SumaComp(cx, cy); . . . – Donde cx y cy son dos complejos previamente definidos e inicializados. ¿De qué tipo debe ser suma? Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 41 Estructura de Computadores 2ITIEI Paso por referencia • Pasa por el uso de un puntero a estructura. Ej: COMPLEJO *pcomplejo; • La función con paso por referencia: COMPLEJO SumaComp2(COMPLEJO *pc1, COMPLEJO *pc2) { COMPLEJO res; res.real = (*pc1).real + (*pc2).real; res.imag = (*pc1).imag + (*pc2).imag; return res; } • La llamada a la función: suma = SumaComp(&cx, &cy); • Ejemplo 3 – Datos de un paciente Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 42 21 Estructura de Computadores 2ITIEI Paso por referencia II • Operador -> (flecha) COMPLEJO SumaComp2(COMPLEJO *pc1, COMPLEJO *pc2) { COMPLEJO res; res.real = pc1->real + pc2->real; res.imag = pc1->imag + pc2->imag; return res; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 43 Estructura de Computadores 2ITIEI Vectores de estructuras • Problema: Producto escalar de dos vectores de complejos – Enfoque 1: 4 vectores (real+imag, 1er vector, real+imag, 2º vector) – Enfoque 2: Dos vectores de tipo COMPLEJO COMPLEJO vector_complejo[10]; • Para acceder a los miembros: vector_complejo[0].real vector_complejo[0].imag . . . vector_complejo[9].real vector_complejo[9].imag • • = 4.3; = 23.27; = 2.1; = 3.7; Nótese que los índices van de 0 a N-1 Ejemplo 4 – Producto escalar de dos vectores de números complejos Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 44 22 Estructura de Computadores 2ITIEI Vectores dinámicos de estructuras • Se reserva memoria para un vector de estructuras: PACIENTE *pp; pp = (PACIENTE *)calloc(np, sizeof(PACIENTE)); • De la misma forma que para cualquier otro tipo. Pe. int int *pi; pi = (int *)calloc(n, sizeof(int)); Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 45 Estructura de Computadores 2ITIEI Recomendaciones/advertencias • • • Declarar las estructuras usando typedef Si se tiene un puntero a estructura usar -> en lugar de (* ) Con vectores de estructuras tener en cuenta que – pp[3].nombre • Es el nombre del cuarto paciente – pac.nombre[3] • Es la cuarta letra del nombre de un paciente • Si las estructuras son muy grandes, usar paso por referencia en las llamadas a funciones Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 46 23 Estructura de Computadores 2ITIEI Archivos • Se utilizan para almacenar datos de forma permanente • Soportes de gran capacidad • Indicados en nuestro caso (programadores) para – Disco duro, CD, DVD, etc. – Aunque de limitado tiempo de acceso – Alimentar programas que requieren gran cantidad de datos – Recoger resultados de programas que vuelcan muchos datos – Almacenar los resultados de forma permanente • Tipos de archivo – Texto ( .txt) : Legibles por humanos (Notepad, Word, etc) – Binarios (.dat): no legibles. Ocupan menos espacio en disco Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 47 Estructura de Computadores 2ITIEI Abrir un archivo • Para abrir un archivo se usa la función fopen FILE *fopen(char *Nombre_competo_del_archivo, char *modo); • El nombre completo es – Una cadena de caracteres con el nombre y el camino completo • Caso de no estar el archivo en el directorio de trabajo • El modo es otra cadena de caracteres – Indica el tipo de operación a realizar en el fichero • r (lectura), r+ (lectura/escritura), • w (escritura), w+ (escritura/lectura), • a (append), b (binary) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 48 24 Estructura de Computadores 2ITIEI Abrir un archivo II • La función fopen – Devuelve un puntero a una estructura tipo FILE (estructura de control) – Devuelve NULL si el fichero no existe (Comprobar) • O si no tenemos permiso de acceso #include <stdio.h> int main(void) { FILE *pfich; . . . pfich = fopen(“pepe.txt”, “r+”); if (pfich == NULL) { printf(“Error: No se puede abrir el archivo.\n”); } else { /* Archivo abierto correctamente. Se puede trabaja con él */ . . . fclose(pfich); } return 0; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 49 Estructura de Computadores 2ITIEI Abrir un archivo III • Especificación de camimos – En Windows pfich = fopen(“c:\\dani\\datos.m”, “a”); – En Linux pfich = fopen(“/home/dani/datos.m”, “a”); Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 50 25 Estructura de Computadores 2ITIEI Cerrar un archivo • Una vez utilizado – Para no perder datos – Porque el número de ficheros abiertos (simultáneamente) está limitado • La función fclose int fclose(FILE *puntero_al_archivo); • Devuelve – 0 : si se ha cerrado con éxito – -1: si ha ocurrido algún error if (fclose(pfich) != 0) { printf(“Error al cerrar el archivo.\n” “es posible que se haya producido una pérdida de datos”); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 51 Estructura de Computadores 2ITIEI Lectura/escritura de/a archivo • Se usa fprintf/fscanf (similares a prinf/scanf) int fprintf(FILE *puntero_al_archivo, const char *cadena_de_formato); int fscanf(FILE *puntero_al_archivo, const char *cadena_de_formato); • donde – Puntero_al_archivo: puntero devuelto por fopen – Cadena_de_formato: idéntica a las cadenas formato de printf – … : Número variable de parámetros • Ejemplo fprintf(pfich,”El valor de %d en hex ex %x\n”, 15, 15); Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 52 26 Estructura de Computadores 2ITIEI Lectura: fscanf • Devuelve el número de variable leídas correctamente. Ej fscanf(pfich,”%d”, &num); – Devuelve 1 si consigue leer la variable – Devuelve 0 si hay error de formato – Devuelve EOF si final de fichero • La siguiente llamada fscanf(pfich,”%d %d”, &n1, &n2); – Devuelve 2 si consigue leer ambas variables – Devuelve 1 si sólo consigue leer una – Devuelve 0 si no consigue leer ninguna • Y EOF caso de final de fichero con ninguna variable leída Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 53 Estructura de Computadores 2ITIEI Otras funciones de entrada/salida a archivo • Ejemplo de uso de fscanf – Lee números en coma flotante • Otras funciones de E/S int fgetc(FILE *puntero_a_archivo); int ungetc(char c,FILE *puntero_a_archivo); int fputc(int carácter, FILE *puntero_a_archivo); char *fgets(char *cadena, int tam_cadena, FILE *puntero_a_archivo); int fputs(const char *cadena, FILE *puntero_a_archivo); • Ejemplos 5 y 6 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 54 27 Estructura de Computadores 2ITIEI Archivos binarios • Los archivos de texto tienen caracteres ASCII legibles – Se pueden modificar con cualquier editor de textos (ej: notepad) – Printf(“%d”, var); convierte la variable var a 5 caracteres ASCII • Los archivos binarios no son legibles – Contienen una imagen exacta de la memoria, esto es, lo números quedan codificados en binario. Ej 10 (d) -> 0000 1010 (0A H) – Sizeof(var), da el número de bytes de la variable • (char: 1byte, short int: 2 bytes, int: 2/4 bytes, long: 4/8 bytes) – No son legibles por humanos – No son portables (de MSDOS a Windows/Linux) • Almacenamos la estructuras en archivos binarios – Por cuestiones de tamaño – Por cuestiones de velocidad (lectura/escritura) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 55 Estructura de Computadores 2ITIEI Almacenando estructuras en archivos de texto • Se almacenan los datos – Campo a campo – Usando fprintf • Ejemplo 7 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 56 28 Estructura de Computadores 2ITIEI Almacenando estructuras en archivos binarios • Se almacena toda la estructura de una “tacada” – Usando fwrite – En un archivo abierto en modo binario size_t fwrite(void *pestructura, size_t tamaño, size_t numero, FILE *archivo); – Donde • size_t es lo mismo que unsigned long int • void * es un puntero genérico – Y • • • • pestructura: puntero a la base de la estructura tamaño: tamaño de la misma (se obtiene con sizeof) numero: El número de estructuras a escribir archivo: Puntero al archivo de de escritura – La función devuelve el número de estructuras escritas Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 57 Estructura de Computadores 2ITIEI Leyendo estructuras de archivos binarios • Se lee toda la estructura de una “tacada” – Usando fread – En un archivo abierto en modo binario size_t fread(void *pestructura, size_t tamaño, size_t numero, FILE *archivo); – La función devuelve el número de estructuras leídas • Comprobar que es igual al segundo parámetro – numero • Ejemplos 8 y 9 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 58 29 Estructura de Computadores 2ITIEI Programación avanzada en C Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 59 Estructura de Computadores 2ITIEI Estructuras de datos avanzadas (complejas) • Árboles y Listas Enlazadas • Unidad de información: Nodo. Dos partes: – Crecen según las necesidades del programa – Área de datos. Típicamente un struct – Área de enlace. Punteros a los nodos siguientes • Y/o a los nodos anteriores. Ej: • enlace 0x1234 datos 27 typedef struct nodo{ struct nodo *psiguiente; int num; } NODO; El ejemplo tiene: – Un dato sencillo (tipo int) – Un puntero a una estructura del mismo tipo • Definición recursiva Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 60 30 Estructura de Computadores 2ITIEI Forma de trabajo • Las estructuras se crean con – calloc • Una vez utilizadas se liberan con – free • Las distintas estructuras – No necesitan estar en posiciones consecutivas de memoria Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 61 Estructura de Computadores 2ITIEI Listas y árboles • Lista NULL 3 27 1 NULL • Árbol NULL Juan Pepe NULL NULL NULL Paco María NULL Lola Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 62 31 Estructura de Computadores 2ITIEI Listas y árboles II • Listas – Área de enlace: Un punteno, al siguiente nodo – NULL: en el último de la lista • Árboles – Área de enlace: Dos punteros, a los nodos sucesores – NULL: cuando no hay sucesor en la rama correspondiente – El árbol de la figura es un árbol binario. • Puede hacerse de tantas ramas como se desee Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 63 Estructura de Computadores 2ITIEI Maratón popular • Los datos de un corredor en un nodo – En el área de datos del nodo #define TAM_NOM_CORR 100 typedef struct corredor { mombre[TAM_NOM_CORR]; unsigned int dorsal; unsigned int tiempo; } T_CORREDOR; • /* Nombre del corredor */ /* Dorsal del corredor */ /* Tiempo empleado en la carrera */ El nodo #define TAM_NOM_CORR 100 typedef struct nodo_corredor { struct nodo_corredor *psig_nodo; T_CORREDOR dat_corredor; } T_NODO_CORREDOR; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 64 32 Estructura de Computadores 2ITIEI Declaración del nodo • Es una definición recursiva – Uno de los campos de la estructura – declara un puntero a una estructura del mismo tipo • nodo_corredor typedef struct nodo_corredor { struct nodo_corredor *psig_nodo; • Ojo con la siguiente definición: Es incorrecta ! #define TAM_NOM_CORR 100 typedef struct nodo_corredor { T_NODO_CORREDOR *psig_nodo; T_CORREDOR dat_corredor; } T_NODO_CORREDOR; Cesáreo Fernández Martínez /* Error */ UPCO- ICAI Departamento de Electrónica y Automática 65 Estructura de Computadores 2ITIEI Listas con/sin nodo cabecera • Inserción por la cola en una lista – sin nodo cabecera Lista vacía NULL pprimer_nodo NULL 1 pprimer_nodo NULL pprimer_nodo Cesáreo Fernández Martínez 1 27 UPCO- ICAI Departamento de Electrónica y Automática 66 33 Estructura de Computadores 2ITIEI Listas con/sin nodo cabecera II • Inserción por la cola en una lista – con nodo cabecera. El nodo cabecera no tiene datos Lista vacía NULL pcabecera Lista con un nodo NULL pprimer_nodo 1 – El programa es más regular que en el caso anterior Cesáreo Fernández Martínez Estructura de Computadores UPCO- ICAI Departamento de Electrónica y Automática 67 2ITIEI Operaciones con la lista • Operaciones comunes: – Inicializar la lista – Recorrer todos los nodos – Insertar nodos • Al principio de la lista • Al final de la lista • Entre dos nodos de la lista – Buscar un nodo – Eliminar un nodo Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 68 34 Estructura de Computadores 2ITIEI Inicializar la lista • Operaciones a realizar en pseudocódigo T_NODO *IiniLista () { Asignar memoria para el nodo cabecera puntero al siguiente nodo del nodo cabecera = NULL devolver la dirección del nodo cabecera } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 69 Estructura de Computadores 2ITIEI Inicializar la lista II • En código C T_NODO_CORREDOR *IniLista (void) { T_NODO_CORREDOR *pcabecera; p_cabecera = (T_NODO_CORREDOR *)calloc(1, sizeof(T_NODO_CORREDOR)); if (pcabecera == NULL) { printf(“Error, no queda memoria\n”); exit(1); } pcabecera->psig_nodo = NULL; return pcabecera: } – Reserva espacio para el nodo cabecera – Puntero a siguiente = NULL – Devuelve puntero a la base de la lista • Para que main pueda pasarlo al resto de funciones Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 70 35 Estructura de Computadores 2ITIEI Recorrer la lista • Se recorre la lista para – Imprimir los elementos de la misma – Buscar un elemento – Eliminar un nodo • Recorrer la lista en pseudocódigo pnodo_act = dirección de cabecera mientras el puntero al siguiente nodo al que apunta pnodo_act no sea NULL pnodo_act = dirección del siguiente nodo procesar el nodo al que apunta pnodo_act fin mientras Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 71 Estructura de Computadores 2ITIEI Imprimir la lista • En código C void ImprimeLista(T_NODO_CORREDOR *pcabecera) { T_NODO_CORREDOR *pnodo_act; /* Se recorre la lista imprimiendo cada uno de los nodos*/ pnodo_act = pcabecera; while(pnodo_act->psig_nodo!= NULL){ pnodo_act = pnodo_act->psig_nodo; ImprimeCorredor( &(pnodo_act->dat_corredor) ); } } – Puntero actual a la cabecera – Puntero a siguiente != NULL • Puntero actual a siguiente (se salta el primer nodo: vacío) • Imprime datos del corredor (campo de datos del nodo) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 72 36 Estructura de Computadores 2ITIEI Imprimir corredor • En código C void ImprimeCorredor(T_CORREDOR *pcorredor) { int horas, minutos, segundos; segAhms(pcorredor->tiempo, &horas, &minutos, &segundos); printf("Nombre: %s, dorsal: %d, tiempo: %02d:%02d:%02d\n", pcorredor->nombre, pcorredor->dorsal, horas, minutos, segundos); } – Se llama a segAhms • Para convertir el tiempo total en segundos a : horas, minutos y segs – Esta función devuelve tres resultados • Usando paso por referencia Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 73 Estructura de Computadores 2ITIEI Función segAhms • Una implantación void segAhms(long int tiempo, int *phor, int *pmin, int *pseg) { *phor = tiempo/3600; *pmin = (tiempo-*phor*3600)/60; *pseg = tiempo - *phor*3600 - *pmin*60; } • Otra void segAhms(long int tiempo, int *phor, int *pmin, int *pseg) { *phor = tiempo/3600; *pmin = (tiempo%3600)/60; *pseg = (tiempo%3600)%60; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 74 37 Estructura de Computadores 2ITIEI Inserción de nodos en la lista • Varias formas de insertar – Al principio de la lista – Al final de la lista – En medio de la lista • Para mantenerla ordenada según un cierto criterio • Inserción al principio de la lista Siguiente de pnodo = siguiente de pcabecera Siguiente de pcabecera = pnodo Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 75 Estructura de Computadores 2ITIEI Gráficamente NULL pcabecera 1 pnodo Cesáreo Fernández Martínez 7 UPCO- ICAI Departamento de Electrónica y Automática 76 38 Estructura de Computadores 2ITIEI Es correcto este código ? • Insertar al principio de la lista – ¿ Funciona cuando la lista está vacía ? void InsertaNodoPrinc(T_NODO_CORREDOR *pcabecera, T_NODO_CORREDOR *pnodo) { /* En primer lugar, el nodo que queremos colocar al principio tendrá que apuntar al que antes era el primero de la lista, cuya dirección está en el nodo cabecera */ pnodo->psig_nodo = pcabecera->psig_nodo; /* en segundo lugar hay que hacer que la cabecera apunte al nodo que acabamos de colocar en primer lugar */ pcabecera->psig_nodo = pnodo; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 77 Estructura de Computadores 2ITIEI Inserción de nodos al final de la lista • En pseucódigo pnodo_act = dirección de cabecera mientras que siguiente de pnodo_act no sea NULL pnodo_act = siguiente de pnodo_act fin mientras siguiente de pnodo_act = pnodo siguiente de pnodo = NULL Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 78 39 Estructura de Computadores 2ITIEI Inserción de nodos al final de la lista • En código C AnadeNodo(T_NODO_CORREDOR *pcabecera, T_NODO_CORREDOR *pnodo) { T_NODO_CORREDOR *pnodo_act; /* En primer lugar se busca el último nodo de la lista, que será aquel cuyo puntero psig_nodo sea NULL */ pnodo_act = pcabecera; /* comenzamos por la cabecera */ while(pnodo_act->psig_nodo!= NULL){ pnodo_act = pnodo_act->psig_nodo; } /* cuando lleguemos aquí, pnodo_act contiene la dirección del último nodo de la lista, con lo que podemos añadir el nuevo nodo */ pnodo_act->psig_nodo = pnodo; /* Ahora el que antes era el último elemento de la lista apunta a pnodo, que ahora es el último elemento */ pnodo->psig_nodo = NULL; /* como es el último, no hay nodo siguiente*/ } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 79 Estructura de Computadores 2ITIEI Gráficamente pcabecera 1 NULL pnodo Cesáreo Fernández Martínez 7 UPCO- ICAI Departamento de Electrónica y Automática 80 40 Estructura de Computadores 2ITIEI Inserción de nodos en medio de la lista • En pseucódigo – Para mantener la lista ordenada (dorsal/tiempos) – Dos punteros: actual y anterior • Insertamos entre actual y anterior pnodo_ant = dirección de cabecera pnodo_act = siguiente de cabecera mientras pnodo_act no sea NULL si dorsal de pnodo_act > dorsal de pnodo /* Encontrado */ siguiente de pnodo_ant = pnodo siguiente de pnodo = pnodo_act return /* Hemos terminado */ fin si pnodo_ant = pnodo_act /* Se avanza al siguiente nodo*/ pnodo_act = siguiente de pnodo_act fin mientras /* Si se llega aquí es que el nodo va al final */ siguiente de pnodo_ant = pnodo siguiente de pnodo = NULL Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 81 Estructura de Computadores 2ITIEI Inserción de nodos en medio de la lista • En código C void InsertaNodoOrdenDor(T_NODO_CORREDOR *pcabecera, T_NODO_CORREDOR *pnodo) { T_NODO_CORREDOR *pnodo_act; T_NODO_CORREDOR *pnodo_ant; /* dirección del nodo anterior al que estoy analizando. Es necesario guardarlo para que al insertar pnodo, el nodo anterior al actual apunte a pnodo y pnodo apunte al actual */ pnodo_act = pcabecera->psig_nodo; pnodo_ant = pcabecera; /* Se recorre la lista hasta que se encuentre un nodo cuyo dorsal sea mayor que el del que se va a insertar o hasta que se llegue al final de la lista */ Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 82 41 Estructura de Computadores 2ITIEI Inserción de nodos en medio de la lista II • En código C while( pnodo_act != NULL ){ if( (pnodo_act->dat_corredor).dorsal > (pnodo->dat_corredor).dorsal){ /* Encontré el sitio en el que va el nodo. Ahora sólo falta insertarlo */ pnodo->psig_nodo = pnodo_act; pnodo_ant->psig_nodo = pnodo; return; /* Una vez insertado no hace falta hacer nada más, así que salimos de la función */ } pnodo_ant = pnodo_act; pnodo_act = pnodo_act->psig_nodo; /* Pasamos al siguiente nodo */ } /* Si se llega aquí, es que el nodo va al final */ pnodo_ant->psig_nodo = pnodo; pnodo->psig_nodo = NULL; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 83 Estructura de Computadores 2ITIEI Gráficamente Pnodo_act Pnodo_ant 3 pnodo Cesáreo Fernández Martínez 10 7 UPCO- ICAI Departamento de Electrónica y Automática 84 42 Estructura de Computadores 2ITIEI Búsqueda de un nodo en la lista • Recorrer la lista • En pseudocódigo – Hasta encontrar el nodo pnodo_act = dirección de cabecera mientras siguiente de pnodo_act no sea NULL pnodo_act = siguiente de pnodo_act si los datos de pnodo_act coinciden con los buscados return pnodo_act fin si fin mientras return NULL /* No se ha encontrado */ Cesáreo Fernández Martínez Estructura de Computadores UPCO- ICAI Departamento de Electrónica y Automática 85 2ITIEI En C • Busca número de dorsal T_NODO_CORREDOR *BuscaNodoDorsal(T_NODO_CORREDOR *pcabecera, int dorsal) { T_NODO_CORREDOR *pnodo_act; /* Se recorre la lista comparando el dorsal de cada nodo con el que se está buscando */ pnodo_act = pcabecera; while(pnodo_act->psig_nodo!= NULL){ pnodo_act = pnodo_act->psig_nodo; if( (pnodo_act->dat_corredor).dorsal == dorsal){ /* Encontrado */ return pnodo_act; /* Devuelvo su dirección */ } } /* Si llegamos aquí, es que no hay en la lista un nodo con el dorsal buscado. En este caso se devuelve un NULL para que el programa principal obre en consecuencia */ return NULL; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 86 43 Estructura de Computadores 2ITIEI Eliminar los nodos de una lista • Para eliminar todos los nodos de una lista – Recorrer la lista – Eliminar nodo (cuidado de no perder el puntero al siguiente nodo) • En pseudocódigo pnodo_act = dirección de cabecera hacer pnodo_borrar = pnodo_act pnodo_act = siguiente de pnodo_act liberar pnodo_borrar mientras pnodo_act no sea NULL Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 87 Estructura de Computadores 2ITIEI En C • Secuencia – Primero: mueve el puntero al nodo actual – Borra nodo anterior void LiberaLista(T_NODO_CORREDOR *pcabecera) { T_NODO_CORREDOR *pnodo_act; T_NODO_CORREDOR *pnodo_borrar; /* Nodo que se va a borrar */ /* Se recorre la lista liberando la memoria de cada uno de los nodos*/ pnodo_act = pcabecera; do{ pnodo_borrar = pnodo_act; /* Antes de que pnodo_act apunte al siguiente nodo, lo guardamos para poder liberarlo después */ pnodo_act = pnodo_act->psig_nodo; free(pnodo_borrar); }while(pnodo_act!= NULL); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 88 44 Estructura de Computadores 2ITIEI Eliminar un nodo de la lista – Recorrer la lista • Hasta encontrar el nodo deseado (P.e. dorsal 7) – Enlazar punteros • Anterior y siguiente – Borrar pnodo_ant = dirección de cabecera pnodo_act = siguiente de cabecera mientras pnodo_act no sea NULL si dorsal de pnodo_act == dorsal buscado /* Encontrado */ siguiente de pnodo_ant = siguiente de pnodo_act liberar pnodo_act return 1 /* Hemos terminado */ fin si pnodo_ant = pnodo_act /* Se avanza al siguiente nodo*/ pnodo_act = siguiente de pnodo_act fin mientras /* Si se llega aquí es que no se ha encontrado el nodo */ return 0 Cesáreo Fernández Martínez Estructura de Computadores UPCO- ICAI Departamento de Electrónica y Automática 89 2ITIEI En C – Función: BorraNodoDorsal int BorraNodoDorsal(T_NODO_CORREDOR *pcabecera, int dorsal) { T_NODO_CORREDOR *pnodo_act; T_NODO_CORREDOR *pnodo_ant; /* dirección del nodo anterior al que estoy analizando. Es necesario guardarlo para que al borrar el nodo actual, el nodo anterior al actual apunte al nodo siguiente al actual */ pnodo_act = pcabecera->psig_nodo; pnodo_ant = pcabecera; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 90 45 Estructura de Computadores 2ITIEI En C II /* Se recorre la lista hasta que se encuentre un nodo cuyo dorsal sea igual al buscado o hasta que se llegue al final de la lista */ while( pnodo_act != NULL ){ if( (pnodo_act->dat_corredor).dorsal == dorsal){ /* Encontré el nodo. Ahora sólo falta eliminarlo de la lista y liberar su memoria */ pnodo_ant->psig_nodo = pnodo_act->psig_nodo; free(pnodo_act); return 1; /* Se devuelve 1 para indicar que el corredor se ha eliminado de la lista */ } pnodo_ant = pnodo_act; pnodo_act = pnodo_act->psig_nodo; /* Pasamos al siguiente nodo */ } /* Si se llega aquí, es que no se ha encontrado ningún corredor con el dorsal adecuado. Por tanto se devuelve un cero */ return 0; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 91 Estructura de Computadores 2ITIEI Ordenar (datos) • Organizar datos en • Usando un “ordenador” – Una secuencia específica – Interna. En RAM – Externa. En cinta (en desuso) • Ordenar objetos – Según una de sus características • Campo de la estructura de datos que da la característica • Algoritmos estables – Al ordenar por varios campos – No destruyen el orden previo • Según otro campo (orden previo: ISBN, orden nuevo: autor) - Dentro del mismo autor se mantiene el orden por ISBN Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 92 46 Estructura de Computadores 2ITIEI Algoritmos • Sencillos. Muchas operaciones (n2) – Burbuja – Inserción • Complicados. Menos operaciones (n log n) – Quicksort Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 93 Estructura de Computadores 2ITIEI Burbuja • Simula una burbuja en una columna de agua • Pseudo – Los elementos de menor peso suben arriba (base del vector) desde i=1 hasta N-1 desde j=N-1 hasta 1 si vec[j-1] > vec[j] intercambia vec[j-1] con vec[j] fin si fin desde fin desde • Dos bucles – Interno: Compara pares adyacentes (de final a principio) • Intercambia si el de abajo es menor que el de arriba – En una pasada sube el elemento de menor peso • A la posición 0 del vector (vector[0]) – Resto de pasadas (bucle externo) • Para que vayan subiendo el resto de elementos hasta su posición Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 94 47 Estructura de Computadores 2ITIEI Mejora I • Puesto que en la primera pasada – Ya ha ocupado la posición 0 del vector – No es necesario seguir comparando este elemento desde i=1 hasta N-1 desde j=N-1 hasta i /* se ha cambiado sólo esta línea */ si vec[j-1] > vec[j] intercambia vec[j-1] con vec[j] fin si fin desde fin desde • • El bucle interno ordena cada vez vectores más pequeños El bucle externo – Hasta asegurar la ordenación • ¿Y si queda ordenado en menos pasadas del bucle externo? Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 95 Estructura de Computadores 2ITIEI Mejora II • No es necesario dar tantas pasadas del bucle externo – Levamos variable de estado (bandera): ordenado desde i=1 hasta N-1 ordenado = 1 desde j=N-1 hasta i /* se ha cambiado sólo esta línea */ si vec[j-1] > vec[j] intercambia vec[j-1] con vec[j] ordenado = 0 fin si fin desde si ordenado == 1 break fin si fin desde • Si el vector de partida está prácticamente ordenado – Con los elementos de peso ordenados y al final • Y algún ligero entre medias – Esta versión resulta muy eficiente Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 96 48 Estructura de Computadores 2ITIEI En C main() { int vec[N]={3,4,7,1}; /* Vector a ordenar */ int i, j; /* Índices para los bucles */ int temp; int ordenado; /* Bandera que indica si el vector ya está ordenado*/ • for(i=1; i<N; i++){ ordenado = 1; for(j=N-1; j>=i; j--){ if(vec[j-1] > vec[j]){ temp = vec[j]; vec[j] = vec[j-1]; vec[j-1] = temp; Si el vector de partida está prácticamente ordenado ordenado = 0; – }Con los elementos de peso ordenados y al final } • Y algún ligero entre medias if(ordenado == 1){ – break; Esta versión resulta muy eficiente } } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 97 Estructura de Computadores 2ITIEI Inserción • Comparar las dos cartas a la izquierda. Orden incorrecto • Comparar la siguiente carta (pivote) con las anteriores – Intercambio – Hacen hueco – Inserta (el pivote) int *Ordena(int *pi, int n) { int i,j,t; for(i=1; i<n ; i++) { t = pi[i]; // pivote for(j=i; j>0 && pi[j-1] > t; j--) pi[j] = pi[j-1]; pi[j] = t; } return pi; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 98 49 Estructura de Computadores 2ITIEI Versión recursiva • Selecciona pivote el elemento de la posición 0 • A la vuelta de las llamadas recursivas – Llamada recursiva hasta que queda un elemento – Compara con el pivote – Desplaza a la izquierda hasta encontrar el punto de inserción int *Ordenar(int *pi, int n) { int i,t; if (n > 1) { t = pi[0]; Ordenar(pi+1,n-1); for(i=0; i<n-1 && pi[i+1]<t; i++) pi[i] = pi[i+1]; pi[i] = t; } return pi; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 99 Estructura de Computadores 2ITIEI qsort • Quick sort – Es recursivo – Basado en árboles binarios • Utiliza intercambios en distancias largas – Al contrario de la burbuja (distancias cortas, adyacentes) • Ejemplo 9 7 5 3 1 • C.A.R. Hoare 1962. Cesáreo Fernández Martínez 1 3 5 7 9 Orden: n log n (frente a n2) UPCO- ICAI Departamento de Electrónica y Automática 100 50 Estructura de Computadores 2ITIEI Principio de diseño • “Divide y vencerás” • Se selecciona un elemento del vector: Pivote – A la izquierda se ponen los elementos menores que el Pivote – A la derecha se ponen los elementos mayores que el Pivote • Se repite el proceso – En ambas mitades – Mientras número de elementos > 1 • Hasta completar la ordenación Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 101 Estructura de Computadores 2ITIEI Gráficamente Pivote 7 9 5 3 Pivote 1 Pivote + 1 3 5 + 1 Cesáreo Fernández Martínez 9 + 3 5 7 + 7 9 UPCO- ICAI Departamento de Electrónica y Automática 102 51 Estructura de Computadores 2ITIEI De forma algoritmica • Se selcciona un pivote • Do – P.e. El elemento central del vector – Se busca por la izquierda del pivote • Un elemento a[i] >= pivote – Se busca por la derecha del pivote • Un elemento a[j] <= pivote – Se intercambian los elementos a[i] • i++ , <-> a[j] j-- • Repetir mientras i<j • El proceso se repite en ambas mitades – Mientras número de elementos > 1 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 103 Estructura de Computadores 2ITIEI En pseudocódigo función quicksort(vec, izq, der) i=izq j=der pivote = vec[(i+j)/2] hacer mientras vec[i] < pivote i++ fin mientras mientras vec[j] > pivote j-fin mientras si i <= j intercambiar vec[i] con vec[j] i++ j-fin si mientras i <= j si izq < j // más de un elemento quicksort(izq, j) fin si si der > i // más de un elemento quicksort(i, der) fin si Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 104 52 Estructura de Computadores 2ITIEI Bucles i/j de forma gráfica Pivote = 1 3 7 1 i 8 1 1 7 1 i j 8 3 j 1 7 1 i j 8 3 Pivote = 1 1 1 7 j i Cesáreo Fernández Martínez 8 3 UPCO- ICAI Departamento de Electrónica y Automática 105 Estructura de Computadores 2ITIEI Nótese • A la izquierda del pivote • A la derecha del pivote – Elementos <= que el pivote – Elementos >= que el pivote • El pivote queda insertado – En cualquier posición del vector – En su posición definitiva Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 106 53 Estructura de Computadores 2ITIEI Versión C • Con etiqueta DEBUG – De ayuda a la depuración void quicksort(int *pvec, int izq, int der) { int pivote; int i,j; int temp; i=izq; j=der; pivote = pvec[(izq+der)/2]; #ifdef DEBUG printf("Izq = %d, Der = %d, Pivote = %d ", izq, der, pivote); #endif do{ while(pvec[i] < pivote){ i++; } while(pvec[j] > pivote){ j--; } Cesáreo Fernández Martínez Estructura de Computadores UPCO- ICAI Departamento de Electrónica y Automática 107 2ITIEI Versión C II if(i <= j){ temp = pvec[i]; pvec[i] = pvec[j]; pvec[j] = temp; i++; j--; } }while(i <= j); #ifdef DEBUG printf("i= %d, j= %d\n", i, j); printf("Vec= "); ImpVec(pvec, N); #endif if(izq<j){ quicksort(pvec, izq, j); } if(der>i){ quicksort(pvec, i, der); } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 108 54 Estructura de Computadores 2ITIEI qsort de la librería estandar • Prototipo void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); • Argumentos: – Base del vector – Número de elementos – Tamaño de cada elemento – (*compar): Puntero a la función de comparación • La función de comparación – Recibe: dos punteros a los elementos a comparar – Devuelve: int>0 si primer_elemento> segundo Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 109 Estructura de Computadores 2ITIEI Llamada • La llamada a qsort int comp(const void *pa, const void *pb); main() { int vec[N] = {3,7,1,8,1}; .... qsort((void *)vec, N, sizeof(vec[0]), comp); .... • La función qsort utiliza punteros void – De esta forma es genérica !!! – Puede trabajar con vectores de elementos de cualquier tipo – Únicamente hay que suministrar la función de comparación adecuada – qsort utilizará la función de comparación suministrada Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 110 55 Estructura de Computadores 2ITIEI Funciones de comparación • • Diferentes funciones de comparación Para enteros int comp(const void *pa, const void *pb) { return *((int *)pa) - *((int *)pb); } • Para flotantes int comp(const void *pa, const void *pb) { return *((float *)pa) - *((float *)pb); } • La función de comparación – Convierte los punteros void al tipo adecuado • Realiza el paso contrario al realizado en la llamada a qsort Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 111 Estructura de Computadores 2ITIEI qsort puede ordenar objetos de cualquier tipo • Entre ellos, puede ordenar cadenas de caracteres • Para hacerlo de forma eficiente – Sólo se necesita suministrar la función de comparación adecuada – Se utiliza un vector de punteros – El intercambio de punteros de vector es eficiente • Vector de punteros José Manuel Juan José Pepe Ana María Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 112 56 Estructura de Computadores 2ITIEI Ordenado • Vector de punteros odenado José Manuel Juan José Pepe Ana María Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 113 Estructura de Computadores 2ITIEI Vector de punteros • Definición char *pvect[N]; • // Define un vector de N punteros a cadenas Entrada de datos for(i=0;i<N;i++){ fgets(temp, TAM, pf); // Entrada por fichero if(strlen(temp)>1){ pvect[i]=calloc(strlen(temp)+1, sizeof(char)); if (pvect[i] == NULL) erorr(“No hay memoria”); strcpy(pvect[i], temp); } else break; } pvect[i] = NULL; return i; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 114 57 Estructura de Computadores 2ITIEI Ordenando con quicksort • Reescribir la función quicksort de clase • De forma que en lugar de utilizar – Operadores >, <, >=, <= • Se utilice – No nos gusta la de lib (cambio char* -> void* y viceversa – Una función de comparación de cadenas – strcmp u otra distinta • Práctica de lab. – Para la base de datos de libros – Con entrada por fichero binario – Función de comparación: strcmp • quicksort recibe char **ppcad ! – (ppcad[0] es el primer puntero, ppcad[i] puntero i) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 115 Estructura de Computadores 2ITIEI Ejemplo de doble puntero: Burbuja void Burbujap(char **ppcad, int ncad) { int i,j; char *ptemp; int ordenado; for(i=1; i<ncad; i++){ ordenado = 1; for(j=ncad-1; j>=i; j--){ if(strcmp(ppcad[j-1], ppcad[j])>0){ ptemp = ppcad[j]; ppcad[j] = ppcad[j-1]; ppcad[j-1] = ptemp; ordenado = 0; } } if(ordenado == 1){ break; } } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 116 58 Estructura de Computadores 2ITIEI Caso de interés práctico • Ordenar cadenas de caracteres – Escaso interés práctico • Ordenar vectores de estructuras • Ejemplos – Por un campo de una estructura (tipo cadena): Gran interés – Biblioteca: Ordenar por autor – CD’s: Ordenar por título • En lugar de vector de punteros a cadenas usamo vecto de punteros a estructuras LIBRO *pvect[N]; CD *pvect[N]; • // Define un vector de N punteros a LIBRO’s // Define un vector de N punteros a CD’s Quicksort recibe LIBRO **ppvec (doble puntero) – Con la función de comparación adecuada. En este caso usamos strcmp(ppvec[i]->libro->autor, ppvec[j]->libro->autor) Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 117 Estructura de Computadores 2ITIEI Casos prácticos: Resumen • Con quicksort debemos – Usar vectores de punteros a estructuras – No vale para listas • Y si se desea la flexibilidad de una lista? – Borrar/añadir nodos • Usar la lista para la entrada de datos – Doblemente enlazada (si queremos usar burbuja sobre la lista). • Para usar quicksort – Crear un vector de punteros de forma dinámica – Ordenar utilizando el vector de punteros Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 118 59 Estructura de Computadores 2ITIEI Árboles Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 119 Estructura de Computadores 2ITIEI Árboles • Basados en nodos – Con dos o más ramas – Dos o más punteros (a nodo siguiente). • Ej: Ascendientes de Paco NULL NULL Juan Pepe NULL NULL Paco NULL María NULL Lola Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 120 60 Estructura de Computadores 2ITIEI Árboles II • • Ideales para organizar datos que forman una jerarquía Ejemplos – Un árbol de carpetas/archivos: Windows, Unix, etc. – Sitio Web: Página principal, páginas hijas – Evaluación de expresiones: (a * b + c) * (d – e / f) a * + b * C - d e / f Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 121 Estructura de Computadores 2ITIEI Terminología • Descendiente directo • Ascendiente directo • Niveles – Cuando un nodo está inmediatamente por debajo de otro – Justo por encima. – Raíz: Nivel 1 – Profundad. Máximo nivel • Nodos hoja – No tienen descendientes – Resto de nodos: Nodos interiores • Grado – Número de descendienes – Grado 2: árboles binarios Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 122 61 Estructura de Computadores 2ITIEI Árboles equilibrados • Equilibrado: cuando para todo nodo – El número de nodos en el subárbol izquierdo, y – El número de nodos en el subárbol derecho Difieren como mucho en 1 • Equilibrado y ordenado – Ideales para realizar búsquedas – En pocos pasos 1 3 4 5 8 6 9 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 123 Estructura de Computadores 2ITIEI Árboles binarios • Conjunto de nodos que – Está vacío, o – Formado por un nodo raíz: más dos subárboles disjuntos • Derecho • Inzquierdo – (si no fuesen disjuntos sería un grafo: útiles para representar REDES malladas) • Nodo (declaración) typedef struct nodo_arbol{ int n; struct nodo_arbol *pder; struct nodo_arbol *pizq; }T_NODO_ARBOL; Cesáreo Fernández Martínez /* Área de datos */ /* Área de enlace */ UPCO- ICAI Departamento de Electrónica y Automática 124 62 Estructura de Computadores 2ITIEI Construir un árbol • • • Manteniendo la altura mímima Manteniendo el árbol equilibrado Replicando algún tipo de estructura física – Topología de una red radial • Equilibrado de altura mínima sin ordenar • Procedimiento recursivo – Llenar por niveles – Se genera la raíz – Se genera el subárbol izquierdo de n/2 nodos • De la misma forma que el árbol completo: mismo algoritmo – Se genera el subárbol derecho de n-n/2-1 nodos • De la misma forma que el árbol completo: mismo algoritmo Cesáreo Fernández Martínez Estructura de Computadores UPCO- ICAI Departamento de Electrónica y Automática 125 2ITIEI Árbol binario en C T_NODO_ARBOL *Arbol(int n) { int ni, nd; /* Número de nodos a la derecha y a la izquierda */ T_NODO_ARBOL *praiz; /* Raíz del árbol que se va a crear */ if(n<=0){ /* Si se desea crear un árbol de tamaño nulo */ return NULL; /* Se devuelve NULL */ }else{ /* Se crea un árbol con 1 nodo raíz, ni nodos a la izquierda y nd a la derecha */ ni = n/2; nd = n-ni-1; /* El -1 es por la raíz */ /* Se crea el nodo raíz */ praiz = (T_NODO_ARBOL *)calloc(1,sizeof(T_NODO_ARBOL)); if(praiz==NULL){ printf("Error: No hay memoria suficiente\n"); return NULL;} /* Se lee el número a almacenar en este nodo */ printf("n= "); scanf("%d", &(praiz->n)); /* Y se termina creando los subárboles izquierdo y derecho */ praiz->pizq = Arbol(ni); praiz->pder = Arbol(nd); return praiz; } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 126 63 Estructura de Computadores 2ITIEI Impresión del árbol • De forma recursiva – Comenzando por el subárbol derecho • Con tantos espacios como indica margen void ImprimirArbol(T_NODO_ARBOL *praiz, int margen) { int n; if(praiz != NULL){ ImprimirArbol(praiz->pder, margen+1); for(n=0; n<margen; n++){ printf(" "); } printf("%d\n",praiz->n); ImprimirArbol(praiz->pizq, margen+1); } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 127 Estructura de Computadores 2ITIEI Resultado de la impresión • Mayor espaciado en las hoja – Menor en los nodos intermedios 7 5 6 1 4 2 3 Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 128 64 Estructura de Computadores 2ITIEI Liberar el árbol • Mediante función recursiva void LiberaArbol(T_NODO_ARBOL *praiz) { if(praiz != NULL){ /* Primero se liberan los descendientes del nodo raíz */ LiberaArbol(praiz->pder); LiberaArbol(praiz->pizq); /* Y a continuación se libera el nodo raíz de este árbol (o subárbol) */ free(praiz); } } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 129 Estructura de Computadores 2ITIEI Programa principal • Crea el árbol y lo imprime main() { int n; T_NODO_ARBOL *praiz; printf("Número de nodos= "); scanf("%d", &n); praiz=Arbol(n); ImprimirArbol(praiz, 0); LiberaArbol(praiz); } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 130 65 Estructura de Computadores 2ITIEI Tablas Hash Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 131 Estructura de Computadores 2ITIEI Tablas hash • • Ideales para buscar de forma rápida Es un vector (tabla) de punteros cabecera a diferentes listas – Cada nodo de la lista tiene • Puntero al siguiente • Campo(s) de información – En índice del vector es el número “hash” • Obtenido en función del campo de información 0 Lista 1 0 0 0 0 0 Cesáreo Fernández Martínez Lista N UPCO- ICAI Departamento de Electrónica y Automática 132 66 Estructura de Computadores 2ITIEI Ejemplo • Tabla de “defines” . Ej : #define N 10 – Objetivo: • Montar la tabla de define’s • Cuando aparece un define. Sustituir por su valor • Nodo: struc nlinst { struct nlist *next; char *name; char *defn; } • // Nombre // Valor La tabla es – Un vector de punteros (de cabecera a diferentes listas) struc #define HASHSIZE 101 struct nlinst *hashtab[HASHSIZE]; Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 133 Estructura de Computadores 2ITIEI La función hash • Devuelve un valor entero positivo función de – Los caracteres de una cadena • “suma ponderada” de los caracteres de la cadena unsigned int hash(char *s) { unsigned int hashval; for (hashval=0; *s != ‘\0’; s++) hashval = *s + 31*hashval; return hashval % HASHSIZE; } • El valor devuelto – Es el índice en la tabla de hash – La cadena buscada sólo puede estar en esta posición de la tabla Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 134 67 Estructura de Computadores 2ITIEI Búsqueda • La búsqueda se realiza sobre – La lista correspondiente a la posición de la tabla de hash – Dada por el índice devuelto por la función hash struct nlist *lookup(char *s) { struct nlist *np; for (np = hashtab[hash(s)]; np != NULL; np = np->next) if(strcmp(s, np->name) == 0) return np; return NULL; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 135 Estructura de Computadores 2ITIEI Install: Introducir un nombre en la tabla • usa lookup para Install – Ver si el nombre está ya en la tabla • Si ya está, la nueva definición anula la antigua – Si el nombre no está en la tabla • Se crea y • Se añade a la lista correspondiente Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 136 68 Estructura de Computadores Install II: 2ITIEI Introducir un nombre en la tabla struct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL ||np->name = strdup(name))== NULL) return NULL; hashval = hash(name); np->next = hashtab[hashval]; hashtab[hashval] = np; } else free((void *) np->defn); if ((np->defn = strdup(defn)) == NULL) return NULL; return np; } Cesáreo Fernández Martínez UPCO- ICAI Departamento de Electrónica y Automática 137 69