Documentos de Académico
Documentos de Profesional
Documentos de Cultura
00 Memoria Dinamica
00 Memoria Dinamica
Memoria Dinámica 9
09.01 Introducción
Al usar arreglos, se estiman sus tamaños en tiempo de programación. En tiempo de ejecución cada arreglo se aloja en un bloque de
memoria RAM, si el tamaño de un bloque es:
1) grande, no se usa todo el espacio reservado y se desperdicia memoria rápida, escasa y cara.
2) pequeño, faltará espacio, lo cual es un severo error lógico, cuya consecuencia es: cancelación del programa por violación de seg-
mento, o se puede producir una corrupción de datos NO DETECTADA, lo cual es un error severo de lógica.
Para resolver estos dos problemas, la lógica del programa debe controlar el tamaño de los bloques en tiempo de ejecución: crear, mo-
dificar (aumentar o disminuir) y eliminar los bloques.
La piscina de memoria dinámica es un área especial para bloques de memoria apuntados por apuntadores, de acuerdo a las necesida-
des en el momento de ejecución. Las funciones de alojamiento de memoria retornan un puntero al inicio de bloque alojado, el progra -
mador debe controlar:
1) No perder la dirección apuntada, porque es la referencia base para todo el bloque
2) No navegar fuera del bloque.
PÁGINA: 1
Lenguaje C++
09.03 Funciones para Alojar Memoria Dinámica
Sirven para alojar, modificar y desalojar bloques de memoria. Use la directiva: #include<cstdlib>
Usaremos un tipo de dato de tipo entero positivo:
size_t // tlipo entero positivo y grande. Se escribe y lee con formato %d. int hace autocasting hacia size_t
Descripción Sintaxis Ejemplo
Alojar un bloque de void * malloc(size_t num); // Alojar memoria para 5 enteros
memoria dinámica con Entrada: num de bytes a ser alojados. int i, *ptr;
tamaño en bytes Proceso: Aloja num bytes en un bloque y asig- i = 5* sizeof(int);
na 0 para números, blanco para caracteres, etc. ptr = (int *)malloc(i); // note el casting (int *) a puntero int
dependiendo del tipo. if (ptr == NULL) { // if (!ptr) {...}
Salida: puntero void *, apuntando al primer printf("No hay suficiente memoria");
byte del bloque alojado; si no es posible alojar, exit(EXIT_FAILURE);
retorna la constante simbólica NULL. Atento se }
reqiere de un casting a apuntador …
Alojar un bloque de void * calloc (size_t num, size_t tam); // Alojar memoria para 5 enteros
memoria dinámica para Entrada: int *ptr;
datos de un tipo espe- num: número de elementos a alojar. ptr = (int *)calloc(5, sizeof(int)); // sizeof(int) = 4
cífico. tam: tamaño (en bytes) de cada elemento. if (ptr == NULL) { // if (!ptr) {...}
Similar a malloc() Proceso: Aloja num elementos de tamaño tam printf("No hay suficiente memoria");
en un bloque y asigna 0 para números, blanco exit(EXIT_FAILURE);
para caracteres, etc. dependiendo del tipo. }
Salida: puntero void *, apuntando al primer …
byte del bloque alojado; si no es posible alojar,
retorna la constante simbólica NULL. Atento se
reqiere de un casting a apuntador
Puede alojar memoria, indistintamente, con malloc() o calloc()
Cambiar el tamaño de void * realloc(void *ptr, size_t num); // Existe el puntero ptr que apunta a un bloque de 5 ente-
un bloque de memoria Entradas: ros, vamos a redimensionarlo a 8 enteros.
previamente asignado ptr: puntero que apunta al bloque a ser realoja- int *ptr...; // apunta a un bloque de 5 enteros.
con malloc( ) o calloc( ) do int i = 8* sizeof(int),
num: número de bytes a ser alojados. ptr = (int *)realloc(ptr, i);
Proceso: ver proceso debajo de esta tabla. if (ptr == NULL) { // if (!ptr) {...}
Salida: puntero (void *) que apunta al primer printf("No hay suficiente memoria");
byte de la nueva memoria alojada; si no es po- exit(EXIT_FAILURE);
sible alojarla retorna la constante simbólica }
NULL. …
Liberar memoria apun- void free( void *ptr ); // ya existe el puntero ptr apuntando a la memoria:
tada por un apuntador Entrada: free (ptr);
ptr: apunta al bloque a liberar.
Proceso: Libera el bloque apuntado por ptr.
Proceso de realloc():
1) Si ptr es NULL, la función actúa como malloc( ), asigna un bloque de tam bytes y retorna un puntero a él.
2) Si se pide disminuir el tamaño del bloque, lo disminuye, el resto es liberado, puede o no retornar el mismo apuntador.
3) Si se aumenta el tamaño del bloque:
Si la memoria es insuficiente para la nueva asignación, la función retorna NULL.
Si hay espacio contiguo al bloque apuntado por ptr, lo amplía y retorna el mismo ptr.
Si NO hay espacio contiguo al bloque apuntado, se aloja un nuevo bloque de tam (bytes), se copian los datos existentes al nuevo
bloque, se libera el bloque antiguo y retorna un puntero al nuevo bloque.
Al área aumentada se le asigna 0 para números, blanco para caracteres, etc. dependiendo del tipo.
En tiempo de ejecución:
• Las variables de todo tipo definidas en la main( ) son alojadas en la RAM de inicio a fin del programa.
• Las variables de todo tipo definidas en funciones (que no son la main) se alojan en la RAM al ser llamadas y se desalojan
inmediatamente al finalizar, para ahorrar memoria.
• Un bloque de memoria se aloja en la piscina y es apuntado por un apuntador:
aloja : p = (int *)malloc(100*sizeof(int)); // espacio para 100 enteros
modifica: p = (int +) realloc(200*sizeof(int)); // aumenta a 200 enteros
libera : free(p;) // elimina el bloque y, p=NULL;
La memoria dinámica NO se libera automáticamente al terminar la función que la crea, como sucede con las variables. Se
libera con free() o al terminar el programa.
PÁGINA: 2
Lenguaje C++
09.04 Arreglo de una Dimensión Utilizando Apuntadores y Alojamiento en Memoria
Dinámica
En lugar de declarar y usar arreglos de una dimensión, trabajaremos con apuntadores a memoria alojada dinámicamente. ¿adios a los
arreglos? Noo, podemos seguir utilizando su notación, además ahora, un apuntador ya tiene su propia área de datos.
Décima aplicación de apuntadores: Apuntar a un bloque de memoria dinámica, escribir y leer datos
#include<cstdio>
#include<cstdlib>
main(){
int n = 3, i, *p, *ip, *pmax;
p = (int *)malloc(n*sizeof(int));
if (p == NULL) { printf("No hay suficiente memoria"); exit(EXIT_FAILURE);}
printf("Ingresará 3 números\n");
// Notación de arreglos con índices // Notación con apuntadores
pmax = p + n;
for(i=0; i<n; i++) { for(ip = p; ip<pmax; ip++) {
printf("Ingrese un entero: "); printf("Ingrese un entero: ");
scanf("%d", &p[i]); scanf("%d", ip);
printf("Entero guardado: %d\n", p[i]); printf("Entero guardado: %d\n", *ip);
} }
free(p);
}
/* 09_01.c: Ingresar temperaturas diarias, a medida que las ingresa muestre todas las temperaturas ingresadas anterior-
mente; termina cuando se ingresa temperatura 0.*/
#include <iostream>
using namespace std;
#include <cstdlib>
int *leer (int *n, int *pt); // Lee las temperaturas y las almacena en la memoria dinámica, termina con 0
void escribir(int n, int *pt); // Escribe las temperaturas desde la memoria dinámica.
int main() {
int n = 0, *pt = NULL; // se asigna NULL, porque realloc(pt ..) lo utilizará en leer()
while(pt = leer(&n, pt)) escribir(n, pt); // pt es entrada y salida de leer(), n es el número de temperaturas leídas
// se ingresa &n para cambiarla en leer() y ver el cambio en la main()
free(pt); // libera la memoria
}
int *leer(int *n, int *pt){
int t;
cout << "\nIngrese una temperatura, <para terminar: 0>: ";
cin >> t; // Lee una temperatura
if(t==0) return NULL;
(*n)++;
pt = (int *)realloc(pt, *n*sizeof(int)); // aloja la memoria necesaria: *n = número de elementos
if(pt==NULL) { // cancela el programa
cout << "No hay suficiente memoria";
exit(EXIT_FAILURE);
}
*(pt+*n-1) = t; // guarda la temperatura ingresada al final, puede usar pt[*n-1] = t;
return pt;
}
void escribir(int n, int *pt){ // imprime la lista de temperaturas ingresadas
int *pmax = pt+n;
cout << "Valores ya ingresados: ";
while(pt<pmax) cout << endl << *pt++;
}
Salida:
Ingrese una temperatura, <para terminar: 0>: 1
Valores ya ingresados:
1
Ingrese una temperatura, <para terminar: 0>: 2
Valores ya ingresados:
1
PÁGINA: 3
Lenguaje C++
2
Ingrese una temperatura, <para terminar: 0>: 3
Valores ya ingresados:
1
2
3
Ingrese una temperatura, <para terminar: 0>: 0
PÁGINA: 4
Lenguaje C++
// 09_02a.c: Leer, alojar en memoria dinámica e imprimir una matriz utilizando un apuntador
Ejemplo de Salida:
Matriz cargada:
10 20
30 40
50 60
#include<iostream>
#include <cstdlib>
using namespace std;
void leerMN(int *m, int *n);
int *leerP(int m, int n);
void imprimir(int m, int n, int *p);
int main(){
int m, n, *p ;
cout << "\nUso de punteros y funciones\n";
leerMN(&m, &n);
p = leerP(m, n); // definir memoria dinámica y leer datos
imprimir(m, n, p);
free(p);
}
void leerMN(int *m, int *n){
cout << "\nIngrese el número de elementos de la primera dimensión: ";
cin >> *m;
cout << "Ingrese el número de elementos de la segunda dimensión: ";
cin >> *n;
}
int *leerP(int m, int n){
int i, j, *p, *pp; // pp: puntero índice
pp = p = (int *)malloc(m*n*sizeof(int));
if(p==NULL) {cout << "No se pudo alojar la matriz en memoria"; exit(EXIT_FAILURE);}
// Lectura de datos
cout << endl;
for (i=0; i < m; i++)
for (j=0; j < n; j++){
cout << "Para la fila " << i+1 << ", ingrese el número " << j+1 << ": ";
cin >> *pp++; // ingrese: 10 20 30 40 50 60
}
return p;
}
void imprimir(int m, int n, int *p){
int i, j;
cout << "\nMatriz cargada:\n";
for(i=0; i<m; i++) {
for (j=0; j<n; j++) cout << *p++ << "\t"; // *p++ ≡ p[i*n+k]
cout << endl;
}
}
Atento: No existe físicamente una matriz bidimmensional; es simulada para mostrarla al usuario.
No podemos escribir pp[i][j] // error de compilación.
Pero podemos escribir el equivalente: *(p+i*n+j) ≡ p[i*n+j]
Algunos casos de uso que simulan la notación bidimensional:
Matiz pp (en 2 Equivalente físico lineal con un apuntador p (existente)
dimensiones
Notación linealizada (lento) Desplazamiento físico con apuntador índice (rápido)
lógica e inexistente)
Lectura scanf("%d", &pp[i][j]); scanf("%d", p+i*n+j); scanf("%d", p++);
scanf("%d", &p[i*n+j]);
Escritura printf(" %d\t", pp[i][j]); printf(" %d\t", *(p+i*n+j)); printf(" %d\t", *p++);
printf(" %d\t", p[i*n+j]);
PÁGINA: 5
Lenguaje C++
Forma B: Alojamiento (en una dimensión) que simula una matriz
Si somos forzados a usar la notación de matriz [fila][columna] alojada en una dimensión -no hay otro modo-, debemos hacer un artificio
que consumirá memoria y utilizará apuntadores; ejemplo:
10 20
p= 30 40
50 60
1) Concepto: Definimos un apuntador a apuntador para representar a la matriz:
int **p; // equivalente: *(*p)
// p es un apuntador, por lo tanto podemos usar: *p ≡ p[i] // filas
// *(p[i]) es un apuntador, por lo tanto podemos usar **p ≡ p[i][j] // columas por cada fila
2) Alojar memoria para 3 apuntadores que apuntaran a las filas de la matriz:
p p[0]
p[1]
p[2]
p = (int **)calloc(3, sizeof(int *)); // atento a los casting: int **; y al tamaño: sizeof(int *)
3) Para cada fila definir un apuntador a bloques de columnas:
p p[0]
p[1]
p[2]
for(i=0; i<3; i++) p[i] = (int *)calloc(2, sizeof(int)); // se definen 3 memorias, una para cada fila
Implantación física, real, que será interpretada como una matiz p[3][2]:
Integrando todo:
Tarea Código
Definir apuntador a int **p;
apuntador y alojar p = (int **)calloc(3, sizeof(int *)); // atento a los cast : (int **) y sizeof(int *)
memoria para apuntador if(p==NULL) {printf("No se pudo alojar la matriz en memoria"); exit(EXIT_FAILURE); }
a filas
Alojar espacio para filas for(i=0; i<3; i++) {
p[i] = (int *)calloc(2, sizeof(int)); // atento a los cast : (int **) y sizeof(int *)
if(p[i]==NULL) {printf("No se pudo alojar la matriz en memoria"); exit(EXIT_FAILURE); }
}
Lectura de datos for (i=0; i<6; i++)
for (j=0; j<2; j++) scanf ("%d", &p[i][j]);
Escritura de datos for (i=0; i<3; i++){
for (j=0; j<2; j++) printf("%d ", p[i][j]);
printf("\n");
}
PÁGINA: 6
Lenguaje C++
Liberar apuntadores for(i=0; i<3; i++) free(p[i]); // Libera cada elemento p[ ].
free(p); // Libera p
PÁGINA: 7
Lenguaje C++
Los programas anteriores 09_02a .c y 09_02b .c, son dos formas de operar una matriz m x n, hagamos la comparación:
Operación Modo
Utilizando puntero: 08_02a.c Utilizando puntero a puntero: 08_02b.c
Usando main(){ main(){
malloc() 0 calloc() int m, n, *p; int m, n, **p;
leerMN(&m, &n); leerMN(&m, &n);
p = leerP(m, n); p = leerP(m, n);
imprimir(m, n, p); imprimir(m, n, p);
liberar(p); // libera memoria liberar(m, p); // libera memoria
} }
int *leerP(int m, int n){ int **leerP(int m, int n){
int i, j, *p; int i, j, **p;
// Alojar memoria // Alojar memoria
p = (int *)malloc(m * n * sizeof(int)); p = (int **)malloc(m * sizeof(int *));
if(p==NULL) {… exit(...);} if(p==NULL) {… exit(...);}
for (i=0; i < m; i++){
p[i] = (int *)malloc(n * sizeof(int));
if(p[i]==NULL) {… exit(...);}
}
…. …
// Leer datos // Leer datos
scanf("%d", p++); // int *pp = p; scanf("%d", &p[i][j]);
return p; return p;
} }
Usando realloc() void main(){ main(){
int m, n, *p = NULL; int m, n, **p = NULL;
leerMN(&m, &n); leerMN(&m, &n);
p = leerP(m, n, p);
….. p = leerP(m, n, p);
} …...
}
int *leerP(int m, int n, int *p){ int **leerP(int m, int n, int **p){
PÁGINA: 8
Lenguaje C++
Forma AB: Combina las dos forma A y B anteriores, para que los datos de la matriz se alojen en forma contínua:
Forma A: utilizar un apuntador: *q que apunta al inicio del bloque lineal contínuo
int *q = (int *)calloc(3*2, 4);
Forma B: utilizar un apuntador: **p, *p apunta al inicio de cada fila
Podemos usar simultáneamente las dos formas, ejemplo: imprimir los datos de la matriz:
#include<cstdio>
#include<cstdlib>
main() {
int *q = (int *)malloc(6*sizeof(int));
if(q==NULL) {printf("No se pudo alojar a q\n"); exit(EXIT_FAILURE);} // paso 1
int **p = (int **)malloc(3*sizeof(int *)); // paso 2
if(p==NULL) {printf("No se pudo alojar a q\n"); exit(EXIT_FAILURE);}
for(int i=0; i<3; i++){
p[i] = q + i*2; // paso 3
for(int j=0;j<2;j++) {
p[i][j] = (i*2+j+1)*10; // 10, 20, 30, 40, 50, 60 asignar valores
printf("%d %p\n", p[i][j], &p[i][j]); // imprimir valores
}
}
/* for(int i=0; i<3; i++)
for(int j=0; j<2; j++, q++) printf("%d %p\n", *q, q); // produce la misma salida
*/
}
Salida:
10 0x55a1d7a80260 // posiciones contínuas cada 4 bytes
20 0x55a1d7a80264
30 0x55a1d7a80268
40 0x55a1d7a8026c
50 0x55a1d7a80270
60 0x55a1d7a80274
PÁGINA: 9
Lenguaje C++
Problema con cadena de caracteres
// 09_03.c Leer una cadena de caracteres de cualquier longitud
#include<iostream>
#include<cstdlib>
using namespace std;
char *ingresar(int *n);
int main() {
char *p;
int n;
cout << "ingrese una cadena de caracteres de cualquier longitud: ";
p = ingresar(&n); // n = numero de caracteres leídos
cout << n << " caracteres: " << p << endl;
}
char *ingresar(int *n){
// *n = número de caracteres leidos; *p = apuntador a la cadena
*n = 0;
int ch;
char *p;
p = (char *)malloc(1); if(p==NULL) {cout << "No se pudo alojar la variable"; exit(EXIT_FAILURE);}
while((ch=getchar())!=10) {
*(p+*n) = ch;
(*n)++;
p = (char *)realloc(p, *n+1); if(p==NULL) {cout << "No se pudo alojar la variable"; exit(EXIT_FAILURE);}
}
*(p+*n) = '\0';
return p;
}
Ejemplo:
int *p = new int[10]; if(!p) {printf("\nKo:"); exit(EXIT_FAILURE);}
printf("%p, %d\n", p, *p);
Salida:
0x..f0, 0 // p apunta a la dirección 0x..f0, y el primer elemento ≡ 0
Atento:
1) Se libera la memoria dinámica; pero p (es variable estática) continua apuntándola.
2) p, alojada en la memoria estática, cumple las mismas reglas que cualquier otra variable estática.
PÁGINA: 10
Lenguaje C++
Ejemplo: definición, asignación y recorrido de un área dinámica
Usando malloc() y free() Usando new y delete
#include<cstdio>
#include<cstdlib>
main(){
int *p = (int *)malloc(2*4); int *p = new int[2];
// int *p = (int *)calloc(2, 4);
if(p) printf("\nOk:"); else {printf("\nKo:"); exit(EXIT_FAILURE);}
p[0] = 1; p[1] = 2;
for(int i=0; i<2; i++) printf("%d\n", p[i]);
free(p); Delete [ ] p;
}
cout << q[0] << " " << q[1] << endl;
delete [ ]q;
}
PÁGINA: 12
Lenguaje C++
Definir matriz de más de dos dimensiones
Se define en modo análogo que para 2 dimensiones. Se utilizará ***p y p[m][n][k]:
// definir una matriz tridemensional en memoria dinámica, asignar valores e imprimirlos
#include<cstdio>
#include<cstdlib>
int main(){
int m=2, n=3, k=2, t=0;
printf("Matriz de 3 dimensiones: %d x %d x %d\n", m, n, k);
int ***p = new int **[m]; // define el apuntador a filas
if(!p) {printf("No hay memoria para p"); exit(EXIT_FAILURE);}
for(int i = 0; i < m; i++) {
p[i] = new int *[n]; // Para cada fila define los apuntadores a columnas
if(!p[i]) {printf("No hay memoria para p[%d]", i); exit(EXIT_FAILURE);}
for(int j = 0; j < n; j++){
p[i][j] = new int [k]; // Para cada fila/columna define los apuntadores a la tercera dimensión
if(!p[i][j]) {printf("No hay memoria para p[%d][%d]\n", i, j); exit(EXIT_FAILURE); }
for(int r=0; r<k; r++){
p[i][j][r] = t++; // asigno valores
printf("%d, %d, %d: %d\n", i, j, r, p[i][j][r]); // imprimo valores
}
}
}
// delete
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++) delete [] p[i][j]; // delete de *p
delete []p[i]; // delete de **p
}
delete []p; // delete de ***p
}
PÁGINA: 13
Lenguaje C++
09.08 Preguntas de Teoría
1) ¿Cuál es el problema del manejo de la RAM y el esquema (estrategia) para resolverlo?
3) Escriba la sintaxis de las 4 funciones de memoria dinámica. Por si acaso falla el malloc( ), calloc( ) o realloc( ), ¿qué se escribe a
continuación?
2) Escriba un programa que solicite ingresar temperaturas diarias una por una, termina cuando temperatura = 0, luego de ingresar
una temperatura muestre todas las temperaturas ingresadas anteriormente ordenadas ascendentemente.
3) Utilice alojamiento dinámico para una matriz A[m][n][k] de 2x3x2 dimensiones, lea datos e imprímalos.
4) Utilice alojamiento dinámico y un puntero a puntero a puntero (***p) para leer del stdio una matriz p[m][n][k] y luego imprímala.
5) Copie el programa del problema anterior y recorra la matriz con apuntadores para cada dimensión.
PÁGINA: 14