P. 1
Memoria Dinamica - Lenguaje C

Memoria Dinamica - Lenguaje C

|Views: 86|Likes:
Publicado porJavier Tomasini

More info:

Published by: Javier Tomasini on Jul 29, 2012
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

07/29/2012

pdf

text

original

Programaci´n Modular. ETSIT. 1o C.

o Apuntes del profesor Juan Falgueras Cano Curso 2005

2 Memoria din´mica a
Contenido
2. Memoria din´mica a 2.1. Gesti´n din´mica de la memoria . . . . . . . o a 2.1.1. Punteros . . . . . . . . . . . . . . . . . 2.2. Punteros en pseudoc´digo . . . . . . . . . . . o 2.2.1. Utilidad de los punteros . . . . . . . . 2.2.2. Solicitud din´mica de memoria . . . . a 2.2.3. C++ . . . . . . . . . . . . . . . . . . . 2.2.4. Arrays din´micos . . . . . . . . . . . . a 2.3. Estructuras de datos recursivas . . . . . . . . 2.4. Listas lineales de de nodos con simple y doble 2.5. Listas posicionales y ordenadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . enlace, cabeceras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 4 4 4 5 6 6 7 9

2.

Memoria din´mica a
Hasta ahora todos los tipos de datos predefinidos (enteros, reales, caracteres, booleanos) y definidos por el usuario (enumerados, y estructuras de datos arrays y registros) han sido de tipo est´tico. ¿Qu´ significa esto? Que su ubicaci´n y capacidad de almacenamiento quedan a e o r´ ıgidamente definidos desde el momento en que se declaran y no puede cambiar durante el funcionamiento del programa. Esto tiene la ventaja obvia de ser muy f´ciles de localizar en la a memoria interna y tambi´n de simplificar y aligerar el c´digo de los programas ejecutables. e o Con los punteros las cosas no cambian: son est´ticos. Una variable de tipo puntero es una a variable est´tica, pero. . . la memoria a la que apuntan puede que no. a Vamos a ver en este tema c´mo se puede acceder directamente a la memoria interna libre del o sistema y c´mo se pueden conseguir y devolver montones de memoria seg´n se necesiten a lo o u largo de la ejecucion de partes del programa.

2.1.

Gesti´n din´mica de la memoria o a

Las variables en los programas se declaran mediante un tipo (modo de codificaci´n) y un o nombre de variable. As´ para nosotros una variable es un identicador que se refiere a un espacio ı reservado de una manera definitiva (est´ticamente) por el compilador en alg´n lugar (que hasta a u ahora no nos ha interesado). 2.1.1. Punteros

Un puntero es una variable est´tica (como las vistas hasta ahora) pero destinada a a guardar direcciones de memoria de otras varibles. Lo que esto significa es que las direcciones de memoria pueden ser guardadas y manipuladas en nuestros programas. (Ver Figura 2) Una variable puntero (que no lo olvidemos, son en realidad ´ est´ticas siempre) puede guardar una direcci´n de memoria. Las cuestiones ahora son: ¿QUE dia o ´ ´ recci´n de memoria? , ¿COMO obtener direcciones de la memoria interna?, ¿PARA QUE guardar o esas direcciones?

o /* variables */ int *punt. Sin embargo. de manera que y el ıcil programador puede f´cilmente. debe hacerse sabiendo que los contenidos de aqu´llas direcciones son compatibles. RAM . entre ellos. en el sentido de que si declaramos un puntero a entero. por ejemplo. ´l mismo. como las direcciones de cualquier gu´ de tel´foıa e nos. es el hecho de que un puntero o siempre lleva asociado un tipo al que apuntar. Esto es. para el compilador. del e mismo tipo. // ´ int* TPuntEntero. el puntero es tambi´n una variable e e m´s en memoria. un puntero no es s´lo una direcci´n de memoria interna. 33 ? 3 2 1 0 Figura 1: Hasta ahora no nos hemos preocupado de saber d´nde el compilador ubicaba o las variables est´ticas que hemos usado. con peque˜ os errores destruir todo el estado de un sistema o acceder a cualquier a n parte protegida del mismo. Adem´s veremos en alguna ocasi´n la utilidad de poder hacer aritm´tica sencilla con los a o e punteros: sumarle/restarle un n´mero entero. e o u En principio los valores de las direcciones. u por ejemplo. b. a 3FA08 *p *p 33 33 3FA04 3FA00 p 3FA04 p 3FA04 001A2 00000 Figura 2: Aunque un puntero es una variable monol´ ıtica que apunta a una zona de memoria con otras variables. Al estar el puntero ligado a un tipo concreto de u datos. este aumento/disminuci´n de la posici´n se har´ correctamente: el valor del puntero a un o o a 1 decimos “se arriesgan” porque lenguajes m´s modernos (como por ejemplo Java) evitan el uso de punteros a por muchos motivos. es lo mismo Una cuesti´n muy importante respecto a los punteros. el intercambio de contenidos entre dos direcciones. despu´s veremos un sistema de localizar bloques de memoria y finalmente. a No todos los lenguajes de programaci´n se arriesgan a permitir la gesti´n directa de la memoria o o mediante punteros1 . meros o a n´meros posiciones de memoria. por ejemplo.2. tambi´n o o e asociamos con esas direcciones un tipo de datos. en el caso del ordenador. no podemos despu´s asignarle la direcci´n de un n´mero real. Esto va a ser fundamental para ayudar al compilador a disciplinar nuestro programa. son an´nimas y meras referencias geogr´ficas. En C/C++: typedef int *TPuntEntero. a Para responder a estas preguntas veremos primero un m´todo b´sico para obtener direcciones e a de memoria. y que algunos la toman como diferencia o entre mera direcci´n y puntero de verdad. TPuntEntero a.1 Gesti´n din´mica de la memoria o a 2 a=33. con esto e habr´n quedado respondidas las tres preguntas. el de que el uso de punteros es bastante dif´ de proteger.

3).2. Para acceder a lo apuntado por un puntero los lenguajes e de programaci´n disponen de un operador de desreferenciaci´n. int *p = &x. . El operador & es un potente operador de C/C++ que sirve para obtener la direcci´n de memoria de cualquier elemento. la segunda parte es acceder a esa direcci´n. o Por ejemplo. En C/C++. por un puntero. tambi´n de tipo entero. Si sumamos un entero a un puntero la nueva direcci´n e o tiene en cuenta el tama˜ o del tipo de elemento apuntado. o Para acceder a lo apuntado por una direcci´n. Aunque la direcci´n de memoria 0 existe. se le dar´n valores: a a punt = 0. poniendo el operador delante del o o puntero se entiende que lo que queremos es la informaci´n que hay en la direcci´n escrita en ese o o puntero. le podemos asignar la direcci´n d´nde el compilador ha situado a otra variable. En los diagramas un puntero NULL se suele dibujar o a como un enlace a tierra de electricidad. una direcci´n o ıa u o o que se guarda. Hemos usado en el ejemplo anterior el operador &. nos interesa conocer qu´ es lo o e que hay en el sitio al que apunta un puntero. siguiendo el ejemplo anterior.1 Gesti´n din´mica de la memoria o a 3 entero aumentado en uno se incrementar´ en realidad en el tama˜o de un entero apuntando al a n siguiente entero (ver Fig. *p = x. no se usa y en punteros o es la indicaci´n b´sica de que no hay puntero. Si no tuviesemos esta posibilidad. tan s´lo habr´ o ıamos guardado la direcci´n para nada. debemos desreferenciarlo o o indireccionarlo leer lo apuntado por ´l. en C/C++: int x. *(p+1) *p p 321 33 3FA04 3FA08 3FA04 3FA00 00000 Figura 3: Aritm´tica de punteros. Ver Figura 4 p 0 Figura 4: Puntero NULL: enlace a tierra. punt = &i. Un valor muy especial en los punteros es el valor 0 (NULL). no apunta a nada. que es un puntero a entero. punt = &ficha. o o e El tener guardada la direcci´n ser´ algo bastante in´til si tan s´lo fuese eso. n Una vez creada una variable o tipo puntero se inicializar´. significa que a la vez que creamos la variable puntero p. Esto es. El valor 0 en los punteros es la indicaci´n o de que no se apunta a nada.

el contenido de las estructuras se puede copiar. excepto en el lenguaje C. Utilidad de los punteros ¿Qu´ utilidad puede tener el anterior uso de las variables de tipo puntero? e Hasta ahora la introducci´n de la direcci´n de las variables y zonas de memoria con los o o punteros no a˜ade ninguna ventaja.2. Ver a Figura 5 En C el mecanismo que se utiliza es el de decir la cantidad de memoria que se necesita a una funci´n y ´sta devuelve una direcci´n.2.10]. // array de 10 punteros (raro) NOTA: en pseudolenguaje.2 Punteros en pseudoc´digo o 4 copia el contenido de la variable x al lugar (de tipo entero) al que apunta la variable puentero p. 2. la direcci´n del comienzo del lugar d´nde ha reservado ese o e o o o . leerlas. e o Un uso fundamental es el que se da en los lenguajes en los que no existe un paso de par´metros a por referencia. la etiqueta que los compiladores permiten crear para cada variable est´tica: a x = 3. d´nde es el unico mecanismo posible n o ´ para poder modificar par´metros: pasar una copia de la direcci´n a la funci´n y en ´sta indireccionar a o o e y modificar el elemento pasado a trav´s de su direcci´n.. Este mecanismo ya es conocido por nosotros.. Solicitud din´mica de memoria a El principal uso de los punteros est´ en liberar la rigidez de los espacios de memoria para a datos. Punteros en pseudoc´digo o Puntero <TipoApuntado> *PTI.20]. Las variables est´ticas no pueden crecer durante la ejecuci´n de un programa y. Ejemplos: N *puntero_a_natural. // NODO *ENLACE.1.2.2.2. Naturalmente podemos escribir el contenido de la variable x de la forma m´s sencilla como siempre a x = 3. sin embargo no los arrays. se recurre al siguiente mecanismo: a la funci´n/procedimiento se le pasa una copia (siempre) de la direcci´n de la variable (&) y en la o o funci´n se opera con lo apuntado por esa direcci´n (*): de esa forma se accede al contenido de la o o variable que se haya pasado. la segunda forma de obtener direcciones de memoria es la m´s interesante y la a que nos ocupar´ en este tema. necesitaremos a a o algo nuevo. que adem´s es paginable y compara a tible con memoria virtual de disco) llamada arena situada entre la pila y los datos est´ticos. Sin embargo. si nos hace a o falta m´s espacio para guardar m´s datos durante la ejecuci´n de un programa. *LINK. a 2. bastar´ como siempre con u e a usar su nombre. por ejemplo al lenguaje C. //tipo puntero N array_natural[1. etc. Aparte de este modo de uso el conocer las direcciones de las variables est´ticas de los prograa mas no tiene ning´n otro inter´s ya que para modificarlas. // N *arrPunt[1. //array de 20 naturales enteros 2. n Para ello los programas tienen una zona de memoria libre propia (aunque tambi´n en los e sistemas m´s modernos esta memoria se pide a la del sistema. como le ocurre. Los lenguajes que utilizan punteros aportan funciones que localizan y reservan para nuestro programa tama˜os solicitados de memoria interna. El resultado es equivalente en estos casos.

y devuelve la direcci´n d´nde lo ha encontrado: o o p = malloc(sizeof(int)). y la zona que durante la ejecuci´n y de manera est´tica tienen reservadas las variables. que sirve para ser usada y reusada din´micamente durante su ejecuci´n. la cantidad de bytes que se necesitan. C++ C++ utiliza un operadores m´s complejos “new” que deduce el tama˜o necesitado y modifica a n el par´metro puntero que se le pone. a .3. a o o Una vez que hayamos operado con la direcci´n (con el contenido al que apunta esa direcci´n. Otras zonas son las de datos inicialia o zados: constantes y cadenas de mensajes. en muchos lenguajes no es necesario decir la cantidad de memoria que queremos reservar sino s´lo para qu´ tipo de dato queremos apuntar. .2 Punteros en pseudoc´digo o 5 código PROGRAMA datos inicializados datos no inicializados arena pila de programa Figura 5: Zona de memoria “libre” de un programa. pedimos espacio para un n´mero entero. La liberaci´n de los bloques pedidos al sistema es esencial si el o programa ha de seguir iterando y pidiendo en otros sitios nuevos bloques. o o en realidad).. La primera es la que hace las reservas. debemos liberarla. ıa ıan Si malloc no encuentra m´s memoria libre devuelve una direcci´n nula: nul o NIL ´ 0. y “delete” para liberar esta memoria. si queremos que el sistema localice espacio para un n´mero u de tipo entero. reservar´ 10.000 bytes de memoria que comenzar´ en p. sizeof(int) es una funci´n interna de C u o que dice el espacio en bytes que ocupa cualquier tipo. la segunda es la que las libera. 2. free s´lo requiere conocer el lugar d´nde comienza el bloque antes prestado. o 3 Para ver las dem´s funciones que aporta el sistema. Para ello el lenguaje C aporta b´sicamente dos operadores para la gesti´n din´mica de la a o a memoria: malloc y free. Por otro lado la funci´n malloc(. etc. din´mica tambi´n.2. ver la cabecera: <stdlib. No siempre es as´ y el ı sistema usualmente libera todo lo prestado al programa al terminar. o o Curiosamente no requiere que se le indique cu´nto ocupaba aquella reserva: a free(p). vuelve a dejar ese sitio disponible para futuras necesidades de meroria.2. a Estos operadores son los mismos que se utilizan para crear objetos din´micamente y permiten a adem´s la inicializaci´n de los elementos: Por ejemplo a o 2 dado que los punteros llevan asociados el tipo de datos al que apuntan. p = malloc(10000). a e a espacio de memoria2 . y el compilador o e deduce el espacio de memoria que se necesita y asigna esa direcci´n al puntero. las devuelve al sistema3 . malloc busca espacio libre.) s´lo o o requiere conocer ese dato. Pero en general es una t´ctica a muy sana el liberar los bloques de memoria que ya dejan de ser necesarios. La pila es la o a zona. que se usa para el paso de par´metros a las funciones.h>. La funci´n en C para o realizar esto es free. los bloquea. . Por ejemplo.

TEnlace sigui. En C++ el operador new puede llevar despu´s del tipo que crea un valor para inicializarlo: e double *p.i++) cout << datos[i] << ". delete[] parr. para facilitar su uso.. Si un programa requiere diez bloques de memoria en una ejecuci´n habr´ que tener previstas o ıa diez variables de tipo puntero para guardar las direcciones de estos bloques. un o o car´cter. un puntero. *parr.4. N´tese que en C++ la solicitud es siempre por unidades l´gicas: se pide un entero. Para poder formar estas cadenas de bloques lo que se hace es definir una estructura de datos como (en C++) (ver Fig 6): struct TNodo { int dato. La soluci´n est´ en a enganchar un bloque con el siguiente (nuevamente mediante punteros) y o a formar as´ cadenas de bloques todos ligados y acceder a ellos mediante una direcci´n. 2. 2. }. Arrays din´micos a Una inmediata ventaja de la creaci´n de elementos din´micamente es la posibilidad de crear o a arrays del tama˜o que nos haga falta durante la ejecuci´n del programa: n o int n. typedef TNodo *TEnlace.1416). cout << endl. pint = new int.3 Estructuras de datos recursivas 6 int *pint. . delete pint. double *datos.. Pero si durante la ejecuci´n se hubiesen requerido veinte. u cin >> n. cuando sepamos que no se van a utilizar m´s estas estructuras debemos a liberarlas: delete[] datos. TNodo *sigui.3. p = new double (3. o bien se pide un array de n elementos. struct TNodo { int dato. }. cout << "introducir n´mero de elementos: ". el problema es que no se sabe cu´ntos de a esos bloques de memoria necesitaremos. ". parr = new int[10] // . En todos los casos. Estructuras de datos recursivas Aunque se pueda localizar un bloque de memoria. tendr´ o ıamos un problema. ı o el puntero al primer bloque. datos = new double[n]. una ficha.2. // quedan inicializados a 0 for (int i=0. o. i<n.2. En el ultimo caso se tiene que liberar con la a ´ sintaxis especial delete[] loquesea.

ya para acceder al final de ellos es necesario a ıcil n recorrer toda la cadena. . son tres pasos: obtener la memoria. De esta forma.4 Listas lineales de de nodos con simple y doble enlace. e La cuesti´n ahora es que cada vez que se pide un bloque para un nodo tambi´n se est´ gao e a nando espacio para guardar la direcci´n del siguiente nodo o bloque.4. 1 2 nuevo lista 3 new Figura 7: Inserci´n en cabeza. Es por eso que se desarrollan estructuras a a para tratar de evitar estos retrasos y tratar de igualar los tiempos de acceso a todos los nodos de la lista. 2. tel´fono. etc´tera. u o borrar el nodo 5 ´ el 200. seg´n el usuario vaya introduciendo e u interactivamente los datos en el programa. en el ejemplo. el ultimo no tiene a nadie detr´s ´ a y su siguiente es NULL. ir a˜adiendo n n entradas a una agenda personal (nombre. de un s´lo o o puntero podemos colgar la cantidad de nodos que nos vayan haciendo falta durante el programa. De otra manera. Esto es necesario as´ por la propia recursividad de la estructura. u Lista lista Figura 6: Lista simple: tenemos un puntero lista que apunta a un nodo el cual tiene un puntero que apunta al siguiente. apuntar al antiguo o primero y que el antiguo primero apunte al nuevo primero. Algo m´s dif´ es a˜adirlos al final de todos ellos. o u ı Un enlace es un puntero a algo que tiene un enlace a lo que estamos a´n definiendo. se utiliza como a a indicador de siguiente el valor de puntero nulo. Se puede. Listas lineales de de nodos con simple y doble enlace.2. Tambi´n debemos ver c´mo se borrar´ un nodo seg´n el valor del dato o e o ıa u que contenga. a a u Cuando se acaba la secuencia de nodos y ya no se tienen m´s nodos detr´s. email). Se deja como ejercicio la construcci´n de una estructura lineal o lista de nodos enlazados o a los que se les vaya a˜adiendo los nodos al principio. cabeceras De la gesti´n de memoria din´mica mediante listas de nodos enlazados se deduce que la mayor o a dificultad de las estructuras de listas lineales es la de que el acceso a los nodos se hace linealmente m´s y m´s lento conforme aumenta la longitud de la lista. cabeceras 7 Es la primera vez que estamos definiendo una variable (un tipo m´s bien) o un objeto cualquiera en a funci´n de otro a´n no definido. Dependiendo del criterio utilizado para enganchar esos nodos tendremos estructuras lineales o no lineales m´s o menos complejas pero que permitir´n acceder a los nodos seg´n nos interese. Se deja as´ ımismo como ejercicio el borrar un nodo seg´n su posici´n en la lista. Por ejemplo.

apuntando as´ no s´lo a “sigui” e a ı o sino tambi´n a “anterior”. avanzar o un puntero al anterior al punto de inserci´n. 2 3 lista aborrar 1 Figura 9: Borrar el primero. cuando nos pidan la siguiente posici´n el acceso ser´ inmediato. son tres pasos: guardar su direcci´n. liberar el que ı hay que borrar. de manera que si guardamos la posici´n de la ultima visita. buscando. cabeceras 8 new nuevo 1 4 3 lista tmp 2 Figura 8: Inserci´n en mitad o final. u La cabecera siempre tiene que guardar la posici´n del primer nodo de la cadena. o ´ o n seg´n el caso). Y es que lo m´s usual en estructuras de datos. pero adem´s o a puede ser realmente util guardar la posici´n y el n´mero del nodo (posici´n en la cadena) del ´ o u o que se accedi´ la ultima vez: la ultima visita. o ´ ´ a especialmente en las lineales. Esta informaci´n usualmente contiene el total de nodos existentes. es la operaci´nde a o recorrido secuencial (por ejemplo.4 Listas lineales de de nodos con simple y doble enlace. son cuatro pasos: obtener la memoria. El doble enlace mejora el acceso aleatorio a posiciones cualesquiera de la lista en la que ya se recuerde la posici´n de la ultima visita. que puede ser frecuente.2. o En estos casos se consigue tambi´n una gran mejora si podemos acceder a los nodos no s´lo hacia e o adelante sino tambi´n a los nodos que est´n antes que el nuestro. etc. s´lo tendremos optimizado el acceso secuencial (hacia adelante) de los nodos de la lista. de o ´ o a complejidad 1: mirar vel el siguiente nodo del de la ultima visita y poner el valor de la ultima ´ ´ visita en eese siguiente nodo antes de volver. Si guardamos la posici´n y direcci´n del ultimo nodo o ´ o o ´ visitado. Las cabeceras son estructuras de datos que contienen informaci´n relevante acerca de la cadena o de nodos enlazados. en las cadenas de nodos din´micamente enlazados. imprimiendo.). apuntar con la o lista al siguiente (aqu´ hay que modificar la variable de la lista). e . apuntar al siguiente y que el o anterior apunte al nuevo. quiz´s la o a posici´n del ultimo nodo (para facilitar la operaci´n de a˜adido por la cola.

porsterior. guardar la direcci´n del nodo a eliminar. o o desde 1 hasta la longitud o total de elementos de la lista. lista Figura 11: Lista con una estructura de cabecera donde guardar metadatos importantes de la lista. Pero tambi´n es muy importante el caso en el que se guardan los nodos e ordenadamente. la ı a o a de a˜adidos/borrados o la de b´squeda. eliminar. . son cuatro pasos: avanzar hasta el anterior al que haya que borrar. 2.2. dada una posici´n. se devuelve el puntero o la informaci´n o contenida en ese nodo. esto es.5 Listas posicionales y ordenadas 9 3 lista tmp 1 2 aborrar 4 Figura 10: Borrar en medio o al final. Sin embargo a el costo inicial es mayor. Listas posicionales y ordenadas El acceso a los nodos de las listas se suele hacer por la posici´n.5. para que nos interese mantener a los nodos n u ordenados o no. En este caso el acceso posterior puede ser mucho m´s eficiente. As´ que depende de cu´l vaya a ser la operaci´n m´s frecuente. hacer que o el anterior apunte al siguiente.

32 .2.5 Listas posicionales y ordenadas 10 lista nultvis p ultima visita Figura 12: Lista con dobles enlaces y una cabecera para aprovecharlos manteniendo recuerdo de la ultima visita ´ Juan Falgueras Dpto. Lenguajes y Ciencias de la Computaci´n o Universidad de M´laga a Despacho 3.2.

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->