Estructuras de datos En la práctica de la programación de computadoras existen muchos motivos para optimizar el uso de los recursos, llamese tiempo de procesamiento o espacio de almacenamiento. El uso de la memoria dinámica permite contar con medios racionales de almacenar datos de acuerdo a los requerimientos actuales de cada aplicación o ejecución de los programas. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos El lenguaje C provee de funciones para solicitar memoria al sistema en tiempo de ejecución y para liberarla en el momento en que ya no se use. Esta facultad nos permite hacer uso de dicho recurso a nuestro antojo y es junto con el manejo de los apuntadores una de las caracteristicas más poderosas del lenguaje C. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Modelo Modelode delistas listas Estructuras de datos Las funciones son: void *malloc(size_t size); que reserva size bytes y devuelve un apuntador generico(no asociado a ningún tipo de dato). En caso de falla el apuntador es nulo. void free(void *ptr); libera la memoria apuntada por ptr, ptr puede ser de cualquier tipo. Los bloques a liberar debieron ser asignados de manera dinámica. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Un ejemplo del uso de malloc se muestra a continuación: int *arreglo; int longitud; printf(“cuantos elementos quieres guardar?\n”): scanf(“%d”, &longitud); arreglo = (int *)malloc(longitud*sizeof(int)); cast Aquí se esta reservando un arreglo de longitud variable durante el tiempo de ejecución. Se hace un cast y se usa la función sizeof. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 1 Estructuras de datos El cast es la capacidad del lenguaje C de convertir un tipo de dato en otro. La condición es que el nuevo tipo pueda contener al tipo de dato anterior. Por ejemplo, un float puede contener a un entero o a un char, pero un char no puede contener a un float o a un int. Observese lo siguiente: int a, b; float c; a = 5; b = 8; c = a / b; //dara como resultado 0 c = (float)a /(float)b; //dara como resultado 0.625 Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos En el ejemplo de la lámina anterior, primero se realiza la division de a y b como enteros y el resultado es 0 como es natural. Después se convierten tanto a como b en numeros flotantes y se vuelve a realizar la división, ahora al ser los operandos numeros flotantes el resultado será de punto flotante. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos La función sizeof(datatype) devuelve el tamaño en bytes de la memoria requerida para almacenar una instancia de dicho tipo de datos. Podemos usar las palabras reservadas para los tipos de datos simples o las etiquetas que usemos con typedef. También es posible calcular el tamaño de estructuras definidas con la palabra struct. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Ejemplos: a = sizeof(int);//resultara en 2 o 4 dependiendo del hardware typedef struct{ char nombre[10]; float estatura; int edad; }PERSONA; a = sizeof(PERSONA);//resultara en 16 o 18 struct agenda{ char nombre[10]; char telefono[10]; }; a = sizeof(struct agenda);//resultara en 20 Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 2 Estructuras de datos Regresando al ejemplo del arreglo que se declaro de manera dinámica. int *arreglo; int longitud; printf(“cuantos elementos quieres guardar?\n”): scanf(“%d”, &longitud); arreglo = (int *)malloc(longitud*sizeof(int)); Vemos que el número máximo de elementos en el arreglo depende de la variable longitud. Hicimos un cast al resultado de malloc y calculamos el tamaño de un entero. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 3F43 3F44 3F45 00001100 3F46 3F47 3F48 dirección Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Si lo vemos desde el punto de vista del diseño de la computadora tenemos que la memoria tiene dos caracteristicas. Una es que toda localidad de memoria tiene una dirección y dos que puede guardar un valor y solo uno. En la siguiente lámina se muestra esto de manera gráfica. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos La localidad con dirección 3F43 contiene el valor 12 en decimal. Si despúes quisieramos almacenar el 3 decimal tedriamos que cambiar los ultimos 4 bits y el valor de 12 se perderá para siempre. Estructuras de datos valor Estructuras de datos Cuando declaramos una variable en algun lenguaje de programación no hacemos mas que ponerle una etiqueta a una cierta dirección o conjunto de direcciones. 3F43 3F44 3F45 00001100 a 3F46 3F47 3F48 dirección valor etiqueta Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 3 Estructuras de datos Aquí declaramos que las direcciones 3F43 y 3F44 estan etiquetadas con la a. Es lo que sucede con la siguiente linea en C. int a = 12; 3F43 3F44 3F45 00001100 Estructuras de datos a 3F46 3F47 3F48 dirección valor etiqueta Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 3F43 3F44 3F45 Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 00001100 a 3F46 3F47 3F48 dirección etiqueta valor 00001100 Estructuras de datos a 3F46 3F47 3F48 dirección 3F43 3F44 3F45 Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Existen diferentes técnicas para tener acceso a los datos almacenados en la memoria. Uno de ellos es el acceso directo usando la etiqueta. Así pues, podemos decir que hablar de las direcciones o de su etiqueta es lo mismo. Siempre es más fácil recordar una etiqueta que un numero representando dicha dirección. valor etiqueta 3F43 El otro es de manera 00001100 indirecta usando una 3F44 dirección de referencia y 3F45 un desplazamiento. Este 3F46 método se usa en los 3F47 arreglos. A la dirección 3F48 de referencia también se dirección valor le conoce como apuntador. array[0] array[1] array[2] etiqueta Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 4 Estructuras de datos Ahora que conocemos las caracteristicas de la memoria y los metodos para acceder a ella nos concentraremos en el direccionamiento indirecto o por medio de apuntadores. Un apuntador es una variable que como dato contendra una dirección y por tanto su tamaño dependerá de la arquitectura de la computadora con la que estemos trabajando. Es decir varia según el tipo de computadora y no según el tipo de dato. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Para declarar un apuntador la regla es, tipo de dato, un asterisco y el nombre de la variable tipo apuntador. Ejemplos: int *entero;//apuntador hacia numeros enteros struct agenda *registro;//apuntador hacia una estructura agenda POSICION *ptr;//apuntador hacia un tipo definido con typedef Ahora podemos jugar con las dos caracteristicas de una localidad de memoria. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Para declarar un apuntador debemos pensar en el tipo de dato hacia el que va a “apuntar”. Esto es importante a la hora de querer acceder a los datos de la dirección apuntada. Esto porque el compilador marcará un conjunto de direcciones a partir del dato apuntado, i.e., para un flotante considerará 4 bytes consecutivos a partir de la dirección guardada en el apuntador. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Una variable normal como vimos representa una etiqueta de una dirección de memoria y su uso nos da acceso al contenido de dicha posición de memoria pero no sabemos nada respecto a su dirección. Del mismo modo de un apuntador sabemos la dirección pero no podemos saber nada acerca del contenido de dicha localidad de memoria. Para resolver este pequeño inconveniente existen dos operadores el * y el &. Conocidos tambien como operadores de indirección y referenciación. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 5 Estructuras de datos Este ejemplo es similar a tener es el resultado de las siguientes lineas de codigo. int a, *ptr; ptr = &a; 3F43 00001100 3F44 3F45 3F46 3F 3F47 43 3F48 dirección valor a ptr etiqueta *ptr = 12; Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Otra cuestión interesante es el comportamiento de las estructuras cuando las usamos con apuntadores o variables normales. Tomemos el siguiente ejemplo: typedef struct{ char nombre[50]; int edad; float estatura; char sexo; }ENCUESTA; ENCUESTA persona, *puntero; Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos En este caso decimos que ptr apunta hacia la dirección de a. Y tambien podemos decir que queremos que el contenido de la dirección apuntada por ptr sea 12. De esta manera el operador de indirección (*) aplicado a un apuntador nos permite tener acceso al contenido de una localidad y el operador de referenciación (&) aplicado a una variable nos permite conocer su dirección. En el ejemplo anterior guardamos la dirección de la variable a en ptr. Despúes de manera indirecta guardamos en a el valor de 12. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica Estructuras de datos Haciendo la siguiente sentencia puntero = &persona; persona.nombre es equivalente a puntero->nombre. Y resulta indistinto hacer las siguientes operaciones: puntero->nombre = “Jose Luis”; persona.edad = 19; puntero->estatura = 1.72; Y asi sucesivamente, todas las sentencias modificarán el mismo bloque de memoria. Ing. Ing.Jorge JorgeA. A.Hernández HernándezP.: P.:Memoria Memoriadinámica dinámica 6