Memoria dinámica y su uso en C

Anuncio
Tema 18: Memoria dinámica y su uso en C
1
M. en C. Edgardo Adrián Franco Martínez
http://www.eafranco.com
[email protected]
@edfrancom
edgardoadrianfrancom
Estructuras de datos (Prof. Edgardo A. Franco)
• En muchas ocasiones no es posible conocer de antemano la
cantidad de variables necesarias para un programa
computacional.
• Existen aplicaciones que requieren de enormes cantidades de
arreglos o datos por momentos breves en el funcionamiento
del mismo, por lo que no es viable declarar de antemano a
estas como variables, globales o locales de una función. Lo
anterior implica emplear funciones de ANSI C que permiten
reservar memoria de manera dinámica y ampliarla, reducirla
o destruirla en tiempo de ejecución.
• El manejo de memoria dinámica es la base del poder del
lenguaje C y le da la capacidad de crear programas complejos
que emplean grandes cantidades de memoria y los maneja de
manera eficiente.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Introducción
2
• Las variables globales y del programa principal (main) se
almacenan en posiciones fijas de la memoria llamada
memoria de datos.
• Las variables locales se almacenan en el segmento de
memoria llamada pila y existen solo cuando se hace una
invocación a la función que las declaro. También se pueden
declarar variables estáticas locales que también se
almacenan en segmentos fijos de memoria o en la
memoria de datos , sin embargo, también están
disponibles en la función que las declaro.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Todos los programas definen variables que pueden ser
definidas como globales y locales.
3
• Sin embargo, no todas las veces es posible conocer el
numero de variables con el que va a constar nuestro
programa. C ofrece al desarrollador la opción de crear
diferentes tipos de variables de forma dinámica, para crear
tales variables se utilizan funciones como: malloc(),
realloc(), calloc(), y free().
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Todas estas variables comparten una característica en
común, se definen cuando se compila el programa. Esto
significa que el compilador reserva espacio en memoria para
almacenar los valores para estas variables.
• Las regiones de memoria que reservan/liberan estas
funciones son almacenadas en el montículo o heap.
4
(Main y código de funciones empleadas)
Datos
Memoria de
proceso activo
(Variables globales y declaradas dentro del
main)
Pila
Pila
(Stack)
(Variables y datos
de las funciones al
estar en ejecución)
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Código
Montículo
(Heap)
(Variables
dinámicas del
programa)
5
• Por lo regular cuando se diseña un algoritmo, se debe
conocer que elementos de entrada tendrá y cual será la
salida, sin embargo, en algunas ocasiones no se sabe de
forma exacta el numero de variables que requerirá
nuestro algoritmo.
• Por ejemplo, suponga que se van a registrar el numero
de calificaciones de un conjunto de alumnos, para
resolver este problema se podría utilizar una arreglo de
calificaciones, sin embargo, si el numero de alumnos
crece, nuestro programa ya no seria valido, puesto que
no existen los recursos necesarios para almacenar todos
los datos validos.
Para resolver este problema es necesario recurrir al uso de
apuntadores y a la asignación dinámica de memoria.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Asignación dinámica de memoria
6
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• El espacio de memoria asignado a una variable
generada de manera dinámica se crea durante la
ejecución del programa (tiempo de ejecución), al
contrario de las variables declaradas en código, que
el espacio de memoria se les asigna en tiempo de
compilación.
• Una variable que es generada dinámicamente, se
construye (por ejemplo con malloc) y se puede
destruir en tiempo de ejecución (uso de free).
7
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Recordatorio:
Las instrucciones de un programa compilado se
sitúan en segmentos de memoria denominado
segmento de código (memoria de programa). Los
datos del programa, tales como variables globales, se
almacenan en un área denominado segmento de
datos. Las variables locales y el respaldo de los
registros de control del programa se sitúan en el
segmento llamado pila. Y cuando un programa
solicita memoria dinámica para una variable, se le
asigna memoria del segmento denominado
montículo o heap.
8
• malloc() es la forma más habitual de obtener
bloques de memoria dinámica. La función genera o
asigna un bloque de memoria que es el numero de
bytes pasados como argumento.
• malloc() devuelve un apuntador void* al bloque de
memoria asignado, por lo tanto, hay que realizar un
cast al tipo de apuntador requerido, para hacer
buen uso de la memoria o de los datos que se
lleguen a almacenar en dicho bloque de memoria.
Nota: Todas las funciones de asignación dinámica de
memoria se encuentran definidas en la biblioteca
stdlib.h
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Función malloc()
9
void* = malloc( size_t bytes);
• Donde:
• void*:
es el apuntador que almacenará la
referencia o apuntara al bloque de
memoria generado.
• bytes: es el tamaño en bytes del bloque
de memoria que se va a solicitar.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• El prototipo de la función malloc() sería:
10
//otra forma de generar la memoria
//dinámica seria:
c = (char *)malloc( sizeof(char) );
entero = (int *)malloc( sizeof(int) );
flotante = (float *)malloc( sizeof(float) );
doble = (double *)malloc( sizeof(double) );
#include <stdio.h>
#include <stdlib.h>
int main( void ){
char *c;
int *entero;
float *flotante;
double *doble;
//Uso de malloc para generar variables sencillas
c = (char *)malloc( 1 );
entero = (int *)malloc( 4 );
flotante = (float *)malloc( 4 );
doble = (double *)malloc( 8 );
*c = 'c';
*entero = 2378;
*flotante = 128.89172378;
*doble = 18947282.48263;
printf( "valores: caracter %c, entero %d, flotante
%f, doble %lf",
*c, *entero, *flotante, *doble );
free( c );
free( entero );
free( flotante );
free( doble );
return 0;
}
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Por ejemplo:
*c = 'a';
*entero = 10;
*flotante = 3.89172378;
*doble = 1.48263;
printf( "valores: caracter %c, entero %d, flotante %f, doble %lf \n\n",
*c, *entero, *flotante, *doble );
//Importantísimo liberar la memoria
//cuando ya no es requerida
free( c );
free( entero );
free( flotante );
free( doble );
11
int *ptr;
ptr = (int *)malloc( 10 * sizeof(int) );
• Al llamar a malloc() puede ser que no haya suficiente
memoria disponible, entonces, malloc() devolverá
NULL en la operación, por lo tanto, siempre es
conveniente preguntar después de la operación si se
asigno el bloque de memoria.
int *ptr;
ptr = (int *)malloc( 10 * sizeof(int) );
if( ptr == NULL){
printf( "No hay memoria disponible…\n" ); //no utilizar ptr
return; //fin del programa o realizar la acción conveniente
}
//utilizar ptr
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• La función sizeof se utiliza con mucha frecuencia para
referirnos al tamaño de memoria que se va a generar
por las funciones de memoria dinámica. Por ejemplo, si
se requiere reservar un bloque de memoria para un
arreglo de 10 enteros:
12
int tam;
int *ptr;
printf( "Ingresa el tamaño del arreglo " );
scanf( "%d", &tam; );
ptr = (int *)malloc( tam * sizeof(int) );
Nota importante: Los apuntadores que se utilizan para hacer referencia al bloque de
memoria asignado por malloc() son de tipo dinámico y NO es conveniente que
dichos apuntadores apunten a otro lugar antes de liberar el bloque de memoria
asignado ya que se estará perdiendo la referencia al bloque de memoria y no abra
forma de recuperar la referencia ha esta, por lo tanto, la memoria no será liberada y
hasta finalizar el programa.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Si no se conoce el tamaño de memoria que se quiere
reservar al momento de diseñar un algoritmo, dicho tamaño
se puede solicitar al usuario y generar el bloque de memoria
en tiempo de ejecución. Un pequeño ejemplo seria el
siguiente:
13
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Un arreglo bidimensional en realidad es
un arreglo cuyos elementos son arreglos.
Si el nombre de un apuntador
unidimensional es un apuntador sencillo,
entonces, el nombre de un arreglo
bidimensional será un apuntador a
apuntadores sencillos. Para asignar
memoria a un arreglo multidimensional,
se indica cada dimensión del arreglo al
igual que se declara un arreglo
unidimensional.
14
• Para generar al arreglo bidimensional
utilizando memoria dinámica se hace en dos
pasos:
1. Se solicita la memoria para crear un arreglo
de apuntadores que van a apuntar a cada
fila del arreglo.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Uso de malloc() para generar
un arreglo bidimensional
int **arr2d = (int *)malloc( 10 * sizeof(int *) );
1. Se solicita memoria para almacenar el
numero de elementos que va a formar
cada fila o arreglo unidimensional.
arr2d[i] = (int*)malloc( elemFilas * sizeof( int ) );
15
arr2d[0]
arr2d[1]
arr2d[2]
arr2d[3]
arr2d[4]
arr2d[5]
arr2d[6]
Arreglos
unidimensionales
de tamaño n
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
arr2d
Arreglo de apuntadores sencillos
arr2d[7]
arr2d[8]
arr2d[9]
Donde cada arr2d[i] es un apuntador sencillo que apunta a un arreglo
unidimensional de tamaño n.
16
int main(){
int **arr2d, **aux;
int tamFilas, elemFilas,i,j;
int tam;
do{
system( "cls" );
printf( "Ingresa el numero de filas: " );
scanf( "%d", &tamFilas );
}while( !(tamFilas > 0) );
arr2d = (int**)malloc( tamFilas * sizeof( int* ) );
aux = arr2d;
//para asignar el tamaño de cada una de las filas o arreglos
//unidimensionales
for( i = 0; i < tamFilas; i++ ){
do{
printf( "Ingresa un valor valido de elementos de una fila: " );
scanf( "%d", &elemFilas );
}while( !(elemFilas > 0) );
//asignar valores a cada arreglo
arr2D[i]
srand( time(NULL) );
for( j=0; j<elemFilas; j++ ){
arr2d[i][j] = rand()%100;
}
//imprimir los valores
for( j=0; j<elemFilas; j++ ){
printf( "%d ", arr2d[i][j] );
}
printf( "\n");
}
//liberar la memoria
for(i=0; i<tamFilas; i++ ){
free( arr2d[i] );
}
free( arr2d );
return 0;
//dos formas para generar los arreglos unidimensional
//de elementos
//1.arr2d[i] = (int*)malloc( elemFilas * sizeof( int ) );
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
#include <stdio.h>
#include <stdlib.h>
}
//2./* *aux = (int*)malloc( elemFilas * sizeof( int ) );
aux++;*/
17
• Cuando se termina de utilizar un bloque de memoria
previamente asignado por cualquier función de asignación
dinámica de memoria, se debe liberar el espacio de
memoria y dejarlo disponible para otros procesos, esto se
realiza utilizando la función free(). El prototipo la
función free es:
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Uso de free() para liberar la
memoria asignada dinámicamente
void free(void *ptr);
• Donde: *ptr es el apuntador que hace referencia al bloque
de memoria asignado, si ptr es NULL entonces free no
hace nada.
• Si embargo, si ptr es un apuntador mal referenciado, el uso
de free probablemente destruya el mecanismo de gestión
de memoria y provocará un fallo en la aplicación.
18
• calloc() es otra función que permite
obtener memoria dinámica. Al igual que
malloc() devuelve un apuntador void*
que hace referencia al bloque de memoria
generado o NULL si no existe memoria
suficiente para generar el bloque solicitado,
por tal motivo, también es necesario realizar
un cast a un apuntador valido para manejar
los datos que se van a almacenar en el
bloque de memoria asignado.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Función calloc()
19
void* calloc( size_t n, size_t t );
• Donde:
• n: es el numero de datos que se van a almacenar
en la memoria.
• t: es el tamaño de cada elemento, es decir, el
tamaño del tipo de dato.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• El prototipo de la función calloc() es:
20
• Forma de uso:
puntero = (tipo*) calloc( numElem, tamElem );
Donde:
puntero: es un apuntador valido que hace referencia al bloque de
memoria generado.
tipo *: es el cast al un apuntador valido.
numElem: es el numero de elementos que se van a almacenar en el
bloque de memoria.
tamElem: es el tamaño del tipo de dato que se va a almacenar en la
memoria.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Función calloc()
calloc() una cantidad de memoria igual a numElem * tamElem. Es
decir, calloc() asigna memoria suficiente para un arreglo que contiene
numElem elementos con un tamaño tamElem cada uno.
Por ejemplo:
21
int *ptr;
ptr = (int *)calloc( 10, sizeof(int) );
//otra forma de generar la memoria
//dinamica seria:
c = (char *)calloc( 1, sizeof(char) );
entero = (int *)calloc( 1, sizeof(int) );
flotante = (float *)calloc( 1, sizeof(float) );
doble = (double *)calloc( 1, sizeof(double) );
#include <stdio.h>
#include <stdlib.h>
int main( void ){
char *c;
int *entero;
float *flotante;
double *doble;
c = (char *)calloc( 1,1 );
entero = (int *)calloc( 1,4 );
flotante = (float *)calloc( 1,4 );
doble = (double *)calloc( 1,8 );
*c = 'c';
*entero = 2378;
*flotante = 128.89172378;
*doble = 18947282.48263;
printf( "valores: caracter %c, entero %d, flotante %f,
doble %lf",
*c, *entero, *flotante, *doble );
free( c );
free( entero );
free( flotante );
free( doble );
return 0;
}
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Ejemplo:
*c = 'a';
*entero = 10;
*flotante = 3.89172378;
*doble = 1.48263;
printf( "valores: caracter %c, entero %d, flotante %f, doble %lf \n\n",
*c, *entero, *flotante, *doble );
//Importantisimo liberar la memoria
//cuando ya no es requerida
free( c );
free( entero );
free( flotante );
free( doble );
22
• realloc() es la tercera función para obtener
memoria dinámica. También devuelve un
apuntador void* que hace referencia al bloque
de memoria por lo tanto necesario realizar un
cast a un apuntador valido.
• A diferencia de malloc() y calloc(),
realloc() cambia el tamaño de un bloque de
memoria asignado dinámicamente, es decir,
toma como parámetro de entrada un apuntador
*ptr a esa memoria y dependiendo de un
segundo parámetro incrementará o reducirá el
tamaño de dicho bloque de memoria.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
Función realloc()
23
void* realloc(void* ptr, size_t t);
• Donde:
• ptr: es el apuntador que hace referencia a un
bloque de memoria generado dinámicamente.
• t: es el nuevo tamaño en bytes para el bloque de
memoria referenciado por ptr. t pude ser mayor
o menor que el bloque original.
• Se devuelve un apuntador debido a que puede
que realloc() tenga que desplazar el bloque
original para poder modificar su tamaño. Si este
es el caso, se copia la información del bloque
original (hasta t bytes) en el nuevo bloque.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• El prototipo de la función es:
24
puntero = (tipo*) realloc( ptr, nuevoTam );
Donde:
puntero: es un apuntador valido que hace referencia al
bloque de memoria generado.
tipo *: es el cast al un apuntador valido.
ptr: el apuntador que hace referencia a un bloque de
memoria generado dinámicamente.
nuevoTam: es el nuevo tamaño para el bloque de
memoria referenciado por ptr.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Forma de uso:
25
1. En el C99 se especifico que la memoria referenciada
por ptr se libera y se crea un nuevo bloque.
2. El nuevo bloque contiene la misma información que el
bloque original (hasta la longitud especificada por
nuevoTam). Se regresa un apuntador al nuevo bloque
de memoria, sin embargo, el compilador puede
generar el nuevo bloque a partir de la misma
dirección de inicio del bloque anterior, es decir, el
nuevo bloque puede contener la misma dirección de
inicio que el bloque anterior.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
• Hay que tener en cuanta varias consideraciones al
utilizar la función realloc():
26
4. Si no hay memoria suficiente para asignar
nuevoTam bytes, realloc() devuelve un
apuntador NULL y el bloque original permanecerá
intacto.
18 Memoria dinámica y su uso en C
Algoritmia y programación estructurada
Prof. Edgardo Adrián Franco Martínez
3. Si ptr es NULL, realloc() simplemente genera el
bloque de memoria especificado por nuevoTam. Si
nuevoTam es cero, se libera la memoria
referenciada por ptr y la función devuelve NULL.
27
Descargar