Download - Gestión Dinamica de Memoria

Transcript
Page 1: Gestión Dinamica de Memoria

ASIGNACIÓN DINÁMICA DE MEMORIA Y ESTRUCTURAS DINÁMICAS

La asignación dinámica de memoria es una característica de C/C++. Le permite al programador crear tipos de datos y estructuras de cualquier tamaño de acuerdo a las necesidades que se tengan en el programa.

Funciones Malloc() y Free()La función Malloc() Es una de las funciones de asignación de memoria propias del lenguaje de programación C (acrónimo de memory allocation). Cuando se usa malloc () se pasa la cantidad de bytes de memoria que se necesita. Malloc () encuentra y reserva un bloque de memoria del tamaño pedido y regresa la dirección del primer byte del bloque. No hay de qué preocuparse sobre que parte de memoria se usa, ya que esto es manejado automáticamente.

La función malloc () regresa una dirección, y su tipo de retorno es un apuntador a tipo void. ¿Por qué void?. Un apuntador a tipo void es compatible con todos los tipos de datos. Como la memoria asignada por malloc () puede ser usada para guardar cualquiera de los tipos de datos de C, es adecuado el tipo de retorno void.

La función malloc es empleada comúnmente para intentar “tomar” una porción contigua de memoria. Esta definida como:

void *malloc(size_t size);

Lo anterior indica que regresará un apuntador del tipo void *, el cual es el inicio en memoria de la porción reservada de tamaño size. Si no puede reservar esa cantidad de memoria la función regresa un apuntador nulo o NULL

Dado que void * es regresado, C asume que el apuntador puede ser convertido a cualquier tipo.

Por lo tanto:

char *cp;cp = (char *) malloc(100);

intenta obtener 100 bytes y asignarlos a la dirección de inicio a cp.

Es usual usar la función sizeof() para indicar el número de bytes, por ejemplo:

int *ip;ip = (int *) malloc(100 * sizeof(int) );

El compilador de C requiere hacer una conversión del tipo. La forma de lograr la coerción (cast) es usando (char *) y (int *), que permite convertir un apuntador void a un apuntador tipo char e int respectivamente. Hacer la

1

Page 2: Gestión Dinamica de Memoria

conversión al tipo de apuntador correcto asegura que la aritmética con el apuntador funcionará de forma correcta.

Es una buena práctica usar sizeof() aún si se conoce el tamaño actual del dato que se requiere, ya que de esta forma el código se hace independiente del dispositivo (portabilidad).

La función sizeof() puede ser usada para encontrar el tamaño de cualquier tipo de dato, variable o estructura. Simplemente se debe proporcionar uno de los anteriores como argumento a la función.

Por lo tanto:

int i;struct COORD {float x,y,z};struct COORD *pt;

sizeof(int), sizeof(i), sizeof(struct COORD) ysizeof(PT) son también sentencias correctas.

En el siguiente ejemplo se reserva memoria para la variable ip, en donde se emplea la relación que existe entre apuntadores y arreglos, para manejar la memoria reservada como un arreglo. Por ejemplo, se pueden hacer cosas como:

main(){ int *ip, i; ip = (int *) malloc(10 * sizeof(int) ); for (i=0; i<10; i++) cin>>*(ip+i);}

Cuando se ha terminado de usar una porción de memoria siempre se deberá liberar usando la función free(). Esta función permite que la memoria liberada este disponible nuevamente quizás para otra llamada de la función malloc()

La función free() toma un apuntador como un argumento y libera la memoria a la cual el apuntador hace referencia.

La función Free() Al igual que malloc(), free() es una función del lenguaje de programación C, utilizado para liberar la memoria asignada por malloc (). Al usar la función free () se debe tener en cuenta la regla “toda la memoria que se reserve durante el programa hay que liberarla antes de salir del programa” Al usar las funciones malloc () y free () se debe incluir el archivo de cabecera o librería STDLIB.H.

2

Page 3: Gestión Dinamica de Memoria

Ejemplos de aplicación Ejemplo 1 Asigna memoria para un arreglo de 50 enteros

int *numero; numero = (int * ) malloc (50 * sizeof (int));

Ejemplo 2 Asigna memoria para un arreglo de 10 valores float

float *numero; numero = (float * ) malloc (10 * sizeof (float));

Una Buena aplicación de las funciones utilizadas para la gestión dinámica de memoria se puede visualizar en el siguiente programa:

#include <iostream.h> #include <conio.h> #include <stdlib.h> #include <stdio.h> int main() { clrscr(); int *p=NULL; int nbytes=100; p=(int *)malloc(nbytes); if(p==NULL) { cout<<"Insuficiente espacio en memoria\n"; return -1; } cout<< "Bytes Reservados\n\n"; cout<<"Se han asignado " <<nbytes <<" bytes de memoria\n"; free(p); getch(); }

Implementación del uso de Malloc() y Free() Listado de librerías o archivos de cabecera #include<iostream.h> #include<stdlib.h> #include<conio.h> // Definición de la función principal void main() { clrscr(); int n, i; // Definición de la estructura persona struct persona { char nombre[20]; int edad;};

3

Page 4: Gestión Dinamica de Memoria

// Definición del puntero p de tipo persona utilizado para reservar memoria persona *p; cout<<"PROGRAMA QUE GUARDA EL REGISTRO DE PERSONAS"<<"\n"; cout<<"\nNUMERO DE PERSONAS A INGRESAR : "; cin>>n; // Reserva de memoria dinámica a través de malloc () p =(persona *)malloc(sizeof(persona)); // El ciclo for usado para la entrada de los datos de la persona for(i=1;i<=n;i++) { cout<<"\nDIGITE EL NOMBRE " << i <<" : "; cin>>p[i].nombre; cout<<"DIGITE LA EDAD : "; cin>>p[i].edad; cout<<"\n";} clrscr(); cout<<"LISTADO DE PERSONAS REGISTRADAS "<<"\n"; // El ciclo for usado para la impresión o visualización de los datos registrados for(i=1;i<=n;i++) { cout<<" NOMBRE : "<<p[i].nombre<<"\n"; cout<<" EDAD : "<<p[i].edad<<"\n\n"; } getch(); // La función free () libera la memoria asignada al apuntador p free (p); }

Otras funciones para asignar memoria dinámica Aparte de la función Malloc() tratada en la sección anterior, utilizada para la gestión dinámica de memoria, existen dos funciones adicionales para reservar memoria, ellas son: calloc() y realloc().

La Funcione Calloc() A continuación se presentan el prototipo para la definición de la función calloc().

void *calloc(size_t nmemb, size_t size);

“Cuando se usa la función malloc() la memoria no es inicializada (a cero) o borrada. Si se quiere inicializar la memoria entonces se puede usar la función calloc. La función calloc es computacionalmente un poco más cara pero, ocasionalmente, más conveniente que malloc. Se debe observar también la diferencia de sintaxis entre calloc y malloc, ya que calloc toma el número de elementos deseados (nmemb) y el tamaño del elemento (size), como dos argumentos individuales. Por lo tanto para asignar a 100 elementos enteros que estén inicializados a cero se puede hacer: int *ip; ip = (int *) calloc(100, sizeof(int) );

4

Page 5: Gestión Dinamica de Memoria

La Función Realloc() Esta función intenta cambiar el tamaño de un bloque de memoria previamente asignado. El nuevo tamaño puede ser más grande o más pequeño. Si el bloque se hace más grande, entonces el contenido anterior permanece sin cambios y la memoria es agregada al final del bloque. Si el tamaño se hace más pequeño entonces el contenido sobrante permanece sin cambios. El prototipo de definición para la función realloc() es como se presenta a continuación. void *realloc(void *ptr, size_t size);

Si el tamaño del bloque original no puede ser redimensionado, entonces realloc intentará asignar un nuevo bloque de memoria y copiará el contenido anterior. Por lo tanto, la función devolverá un nuevo apuntador (o de valor diferente al anterior), este nuevo valor será el que deberá usarse. Si no puede ser reasignada nueva memoria la función realloc devuelve NULL.

Si para el ejemplo anterior, se quiere reasignar la memoria a 60 enteros en vez de 100 apuntados por ip, se hará:

ip = (int *) realloc ( ip, 60*sizeof(int) );

Aplicación de Calloc

void *calloc(size_t nmemb, size_t tamano);

Adjudica espacio para un array de nmemb objetos, cada cual tiene como tamaño tamano. El espacio es inicializado a cero todos los bits. La función calloc retorna o bien un puntero nulo o bien un puntero al espacio adjudicado. #include <stdio.h> #include <stdlib.h> #include <conio.h> int main( void ) { int *numPtr, i; size_t tamano=0; printf( "Introduzca el tamaño de la lista: " ); scanf( "%d", &tamano ); puts( "Adjudicamos espacio a la lista (con calloc)." ); numPtr = (int *)calloc( tamano, sizeof(int) ); for( i=0; i<tamano-1; i++ ) printf( "%d, ", numPtr[i] = rand() % 100 + 1 ); printf( "%d\n", numPtr[i] = rand() % 100 + 1 ); numPtr = (int *)realloc( numPtr, tamano/=2 ); printf( "Recortamos la lista a la mitad: %d\n", tamano ); for( i=0; i<tamano-1; i++ ) printf( "%d, ", numPtr[i] ); printf( "%d\n", numPtr[i] ); puts( "Liberamos el espacio (con realloc)." ); realloc( numPtr, 0 ); getch(); } //Fin de la implementación de la función Calloc

5

Page 6: Gestión Dinamica de Memoria

Operadores New y Delete El lenguaje C++ cuenta con dos operadores preconstruidos, ellos son: New y Delete, por esta razón no se requiere incluir ninguna librería o archivo de cabecera para utilizarlos.

El operador New: Realiza una labor parecida a la de la función malloc(), asignando un bloque de memoria según sea requerido.

El operador Delete: Libera un bloque de memoria asignada por New en tiempo de ejecución, de manera semejante a como lo hace la función free().

La sintaxis para el uso del operador new es:

Apuntador = new tipo_de_dato;

Este operador hace una petición al sistema operativo para que se le asigne un espacio en memoria, con el tamaño acorde al tipo de datos (vale la pena recordar la función sizeof), si este espacio está disponible, la operación regresa la dirección real que se otorga, en caso de no haber espacio regresa el valor de NULL (0),

La sintaxis para el uso del operador delete es:

delete apuntador;

Cuando los datos reservados dinámicamente son borrados con delete, todavía quedan en memoria. Si repetimos la instrucción cout inmediatamente después de utilizar delete, veremos que todavía se conservan los valores. Si la repetimos de nuevo antes de dejar el programa, cuando el espacio que ocupaban debe haber sido sobrescrito, veremos que ya no es así. Incluso aunque el compilador nos dé los números correctos, no es una buena práctica pensar que esos datos están ahí todavía, porque en un programa dinámico largo la memoria se usará continuadamente.

Cuando se utiliza new para reservar memoria para un vector, el tamaño del vector se sitúa entre corchetes, siguiendo al tipo:

int *intvector; intvector = new int [20];

y se libera: delete [ ] intvector;

La ejecución de este operador provoca que se libere espacio, dejando como valor indefinido, es decir el sistema operativo lo considera como memoria disponible.

6

Page 7: Gestión Dinamica de Memoria

Aplicación de los operadores New y Delete Se presenta un ejemplo como aplicación de los operadores de C++, utilizados para asignar y liberar memoria dinámicamente. “En el listado se declaran las variables index de tipo entero y los apuntadores point1 y point2 ambos de tipo entero. #include <iostream.h> #include <conio.h> main() { clrscr(); int index, *point1, *point2; point1 = &index; *point1 = 77; point2 = new int; *point2 = 173; cout <<"SALIDA DE LAS VARIABLES index, *point1 Y *point2\n\n"; cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; point1 = new int; point2 = point1; *point1 = 999; cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; delete point1; float *float_point1, *float_point2 = new float; float_point1 = new float; *float_point2 = 3.14159; *float_point1 = 2.4 * (*float_point2); delete float_point2; delete float_point1; char *c_point;c_point = new char; delete c_point; c_point = new char [sizeof(int) + 133]; delete c_point; getch(); return 0; }

Implementación de New para verificación de memoria #include <iostream.h> #include <stdlib.h> #include <conio.h> void main() { int *numero;if((numero = new int)==NULL) { cout << "NO hay espacio suficiente\n"; exit(1); }

7

Page 8: Gestión Dinamica de Memoria

*numero=20 ; cout <<"Resultado\n\n"; cout << "El resultado de numero es:"<<*numero ; delete numero ; getch(); }

Otro caso a considerar es la reserva de memoria con el operador new para un arreglo, un ejemplo sería la reserva de memoria para crear un arreglo de 25 elementos de tipo double, en el montículo, puede declararse como:

double *numero; numero = new double[25];

De igual manera en su forma equivalente se tiene:

double *numero = new double[25];

En este ejemplo, se está declarando a número como un apuntador a variables dinámicas de tipo doble; al tiempo que se le asigna el valor retornado por new.

El valor retornado por new es la dirección del inicio de un bloque de memoria del tamaño requerido para almacenar 25 elementos de tipo double.

En caso de que el montículo no disponga del espacio requerido, new retorna el valor NULL (nulo).

8

Page 9: Gestión Dinamica de Memoria

CREACION DINAMICAS DE MATRICES

char ** crea_mat(int fil, int col){ char **vp=NULL; int i, j; int mem=1; /* flag para indicar que hay memoria para todas las filas */ vp=(char **)calloc(fil, sizeof(char *)); /* se reserva memoria para la tabla de punteros intermedia */ if (vp!=NULL){ for (i=0; i<fil && mem; i++){ if ((vp[i]=(char *) calloc(col, sizeof(char)))==NULL){ mem=0; } } if (!mem) { for(j=0; j<i-1; j++){ free (vp[j]); } free (vp); vp=NULL; /* si no hay memoria se devuelve NULL */ } } return vp;}

9

Page 10: Gestión Dinamica de Memoria

int ** Matriz (int fils, int cols){bool error = false;int ** matriz;int f, i;// "matriz" apunta a un vect. de punt. a filasmatriz = new int * [fils];for (f=0; f<fils; f++)// "matriz[f]" apuntara a un vector de int*matriz[f] = new int [cols];return (matriz);}void LiberaMatriz (int **matriz,int fils,int cols){for (int f=0; f<fils; f++)delete [] matriz[f];delete [] matriz;}

10

Page 11: Gestión Dinamica de Memoria

GUIA DE LABORATORIO #1ASIGNACION DINAMICA DE MEMORIA

Funciones: Malloc, Calloc, Free. Operadores New y Delete

OBJETIVO.

Manipular el funcionamiento de los arreglos (unidimensionales y bidimensionales) utilizando asignación dinámica de memoria para el desarrollo de ejercicios en lenguaje C.

ActividadCrear una matriz dinámica de n filas por m columnas(los valores de n y m son introducidos por el usuario), llenar la matriz con números aleatorios comprendidos entre 1 y 100, visualizar en pantalla los elementos de la matriz, copiar los elementos de la matriz a un vector, ordenar el vector aplicando el método de la burbuja, copiar y remplazar los elementos ordenados del vector a la matriz. Visualizar en pantalla los elementos ordenados de la matriz.

Producto a EntregarDocumento en Word que contenga: Portada, introducción a la gestión de memoria dinámica, cuadro comparativo entre las funciones malloc y free y los operadores new y delete, código del programa en C, conclusiones y bibliografía.Nota: Trabajos o programas repetidos se califican con 0.0.

11