Está en la página 1de 14

Lenguaje C++

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.

09.02 Alojar Memoria Dinámica en Tiempo de Ejecución


Al momento de cargar el programa en la memoria RAM para ser ejecutado, conceptualmente sucede lo siguiente:
Programa fuente Programa ejecutable Alojamiento de memoria en la RAM

Datos estáticos para variables Piscina (heap - montón) de


memoria dinámica exclusiva
para bloques de memoria de
apuntadores

int a = 2, b = 1, c, *p; 1) Ubica las variables a, b, c y p


c = a + b; definidas en el programa, en la
memoria de datos estáticos 0x1, 0x2,
p = (int *)malloc(4);
0x3 y 0x4
*p = c+2; // p[0]=c+2
2) Reemplaza las variables por las
printf("%d %d\n", c, direcciones del programa por
*p); direcciones, el ejecutable queda así:
Salida: 3 5 *0x3 = *ox1 + *0x2; // = 3
*0x4 = 0xf0 // malloc(4)
*0xf0 = *0x3 + 2 // = 5
printf("%d\n", *0x3, *0xf0);

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

¿Será posible programar el mismo problema anterior utilizando un arreglo?

Memoria estática (con arreglos) vs dinámica (con apuntadores)


Estática Dinámica
int arr[200][300]… ; int *parr;
El programador estima, por exceso, el número de ele- No se define el número de elementos. el tamaño total del bloque es contro-
mentos y los aloja en un arreglo lado y cambiado en tiempo de ejecución dependiendo de la dinámica de
ejecución, un puntero apunta al inicio del bloque.
El número total de elementos se guarda en otra variable.
El área de memoria ocupada por un arreglo se libera al El área de memoria apuntada por un puntero está activa hasta que sea ex-
salir de la función que lo define o al terminar el progra- plícitamente liberada con free() en cualquier parte del programa o al termi-
ma. narse el mismo.
Es simple. Es un poquito más compleja.
Recuerde que un apuntador se comporta como un arreglo unidimensional, por ejemplo:
int i=6, *p = malloc(100);
Se cumplen las equivalencias:
*(p+i) ≡ p[i]
p+i ≡ &p[i] // si se aplica &

09.05 Arreglo Multidimensional


Sea una matriz pp de 2 x 3:
10 20 30
40 50 60
Podemos alojarla en memoria dinámica de dos modos:

Forma A: Alojamiento linealizado: m[i][j] ≡ pm[k], donde k = i*n+j // n = número de columnas


Alineando los índices del arreglo en una dimensión:
10 20 30 40 50 60 // una fila tras otra

Alojemos memoria dinámica de tamaño 6 = 2 x 3:


int *p = (int *)malloc(2*3*sizeof(int));
Asignemos valores en memoria dinámica:
for(i=0; i<6; i++) p[i] = (i+1)*10; // valores asignados: 10 20 30 40 50 60

Utilice funciones para leer e Imprimir la matriz:


int *q = p; // q: puntero índice.
for(int i=0; i<2; i++){
for(int j=0;j<3;j++) printf(“%d ”, *q++);
printf(“\n”,);
}
10 20 30
40 50 60

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]:

p p[0] p[1] p[2] 10 20 espacio ? 30 40 espacio ? 50 60


p[0][0] p[0][1] p[1][0] p[1][1] p[2][0] p[2][1]

No se garantiza que los datos estén en forma continua


Lectura de datos:
for (i=0; i<3; i++)
for (j=0; j<2; j++) scanf ("%d", &p[i][j]); // ingresa 10, 20, 30, 40, 50, 60
10 20
30 40
50 60
Escritura de datos:
printf("\nNúmeros ingresados\n");
for (i=0; i<3; i++){
for (j=0; j<2; j++) printf("%d ", p[i][j]); // funciona como matriz
printf("\n");
}
Salida: 10 20
30 40
50 60

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

Escribamos el programa anterior utilizando apuntador a apuntador y funciones:


// 09_02b.c: Leer, alojar en memoria dinámica e imprimir matrices utilizando puntero a puntero
#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);
void liberar(int m, int **p);
int main(){ // La función main() controla el programa por que es MAIN
int m, n, **p; // m filas, n columnas y p valores; equivalente: *(*p)
cout << "\nUso de punteros y funciones\n";
leerMN(&m, &n); // m y n se pasan por referencia para que tomen valor
p = leerP(m, n);
imprimir(m, n, p);
liberar(m, p);
}
void leerMN(int *m, int *n){
cout << "\nIngrese el número de elementos de la primera dimensión: ";
cin >> *m; // m es una referencia a memoria
cout << "Ingrese el número de elementos de la segunda dimensión: ";
cin >> *n;
}
int **leerP(int m, int n){
int i, j, **p;
p = (int **)malloc(m*sizeof(int *)); // Alojamientos de memoria para apuntadores a las filas
if(p==NULL) {cout << "No se pudo alojar la matriz en memoria"; exit(EXIT_FAILURE);}
for (i=0; i < m; i++) {
p[i] = (int *)malloc(n*sizeof(int)); // Alojamientos de memoria para cada fila
if(p[i]==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 >> p[i][j];
}
return p; // se retorna el valor del puntero 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[i][j] << "\t";
cout << endl;
}
}
void liberar(int m, int **p){
for (for i=0; i < m; i++) free(p[i]);
free(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 = (int *)realloc(p, m * n * sizeof(int)); p = (int **)realloc(p, m*sizeof(int *));


for (i=0; i < m; i++) {
p[i] = NULL;
p[i] = (int *)realloc(p[i], n*sizeof(int));
if(p[i]==NULL) {… exit(...);}
}
…. ….
// Leer datos // Leer datos
scanf("%d", pp++); // int *pp = p; scanf("%d", &p[i][j]);
return p; return p;
} }
Mostar datos void imprimir(int m, int n, int *p){ void imprimir(int m, int n, int **p){
int i,j; int i,j;
….. …..
printf(" %d\t", *pp++); printf(" %d\t", p[i][j]);
} }
Liberar memoria void liberar(int *p){ void liberar(int m, int **p){
free(p); int i;
} for (i=0; i < *m; i++) free(p[i]);
free(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

Proceso de tres pasos:


1) forma A:
int *q = (int *)calloc(3*2, 4);
2) forma B:
Alojamos memoria para 3 apuntadores (filas) apuntadas por p:
p p[0]
p[1]
p[2]
Int **p = (int **)calloc(3, sizeof(int *));
3) Alojar los datos en forma contínua:
p[0] = q
p[1] = q + 2;
p[2] = q + 4;

q p P[0] p[1] p[2] 10 20 30 40 50 60


p[0][0] p[0][1] p[1][0] p[1][1] p[2][0] p[2][1]

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;
}

09.06 Operadores de memoria dinámica: new y delete


Se puede sustituir a las funciones malloc() y calloc() con el operador new, el cual combina apuntadores con memoria dinámica y pro-
porciona algunas facilidades operacionales, su sintaxis es:
tipo *p = new tipo // Se aloja una (1) dirección en memoria dinámica y p la apunta.
También se puede asignar un valor a la dirección:
tipo *p = new tipo[valor] // valor es una expresión int que indica el número de elementos
No es seguro que el alojamiento sea exitoso, por lo que se debe comprobar:
if(p) printf("\nOk:"); else {printf("\nKo:"); exit(EXIT_FAILURE);}

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

Para liberar la memoria dinámica apuntada (pero no la variable apuntador) usamos:


delete p; // si p apunta a una variable
delete [ ] p; // si p apunta a un bloque

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;
}

// 09_04.c Dos funciones equivalentes para alojar memoria dinámica. Salida:


mem1: 1 2 1 2
mem2: 1 2 1 2
#include<iostream>
using namespace std;
void prin(char d, int *p){
cout << "mem" << d << ": " << p[0] << " " << p[1] << " ";
}
int * mem1(){
int *p = new int[2];
p[0] = 1; p[1] = 2;
prin('1', p);
return p; // retorna un puntero a memoria dinámica
}
/* Atento: es dificil entender la siguiente función:
Debido a que los operadores * y & son inversos para apuntadores:
int a=2, *q=&a;
cout << q << " " << *&q);
cout << q << " " << &*q);
ambos cout dan el mismo resultado, por ejemplo: 0x7ffe29f1366c 0x7ffe29f1366c
¿Es equivalente void mem2(int * &q){…} a void mem2(int q){…}?
No, por dos motivos:
1) void mem2(int q){…} no compilará.
2) El contexto dinámico es distinto, para entender void mem2(int * &q){…}, siga el flujo de ejecución:
1, 2, 3, 4 y 5.
*/
void mem2(int * &q){ // 3) &q = 0xf0, * &q es un apuntador ubicado en 0xf0
q = new int[2]; // 4) q (ubicado en 0xf0) apunta a 0xf1
q[0] = 1; q[1] = 2;
prin('2', q);
}
int main(){
int *p, *q; // 1) q está ubicado en la posición oxf0
p = mem1();
cout << p[0] << " " << p[1] << endl;
delete [ ] p;

mem2(q); // 2) se envía valor de q = no definido 5) q apunta a 0xf1

cout << q[0] << " " << q[1] << endl;
delete [ ]q;
}

Ventaja de new y delete: alijeran la escritura del programador.


PÁGINA: 11
Lenguaje C++
Definir una matriz de dos dimensiones: Se define en modo análogo que para malloc
// 09_05.c Definir una matriz de dos dimensiones en memoria dinámica, asignar e imprimir valores. Ejemplo:
0 1 2
2 3 4
#include<iostream>
using namespace std;
int main(){
int n=2;
int **p = new int *[n]; // define un apuntador de apuntador que apunta a un arreglo de apuntadores a int
// apuntador a lan n filas
for(int i = 0; i < n; i++) {
p[i] = new int [3]; // define columnas de la fila i
p[i][0] = i*2; // asigna valores:
p[i][1] = i*2+1; // 0 1 2
p[i][2] = i*2+2; // 2 3 4
}
for(int i=0; i<n; i++){ // imprimo
for(int j=0; j<3; j++) cout << p[i][j] << " ";
cout << endl;
}
for(int i=0; i<n; i++) delete [ ] p[i];
delete [ ] p;
}

Desventaja de new y delete


No puede cambiar el tamaño de la memoria dinámica, como lo hace realloc(); pero puede complementarlos:
#include<cstdio>
#include<cstdlib>
int main(){
int *p = new int[2], *q = p;
p[0] = 0; p[1] = 1;
for(int i=0; i<2; i++, q++) printf("%d %p\n", *q, q);
p = (int *)realloc(p, 4*sizeof(int));
p[2] = 2; p[3] = 3;
q = p;
for(int i=0; i<4; i++, q++) printf("%d %p\n", *q, q);
delete [ ] p;
}
Salida:
0 0x5561eaaa5e70 // Posición inicial
1 0x5561eaaa5e74
0 0x5561eaaa5e90 // cambia (o no) posición inicial debido al realloc()
1 0x5561eaaa5e94
2 0x5561eaaa5e98
3 0x5561eaaa5e9c

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
}

9.07 Problemas propuestos y resueltos


Es muy placentero anunciar las bases de la solución de todos los problemas de estructuras de datos, tamaños, parametrización y su
implantación en la RAM; los cuales fueron propuestos en los 4 últimos capítulos:
Problema Solución
1 Decidir el número de elemenos de un arreglo unidimensional: Si se define en tiempo de compilación Memoria dinámica +
puede ser grande y se desperdicia RAM; si es pequeño falta memoria y pueden ocurrir errores de apuntadores
corrupción de datos no detectados o cancelar la ejecución por violación de segmento.
2 Manejar arreglos arr de más de una dimensión: Al definir una función que recibe arr se exige que se Apuntadores
especifique el número de elementos (en constante) a partir de la dimensión 2:
...
fun(3, DOS, CUATRO, arr); // llama a fun( ) y pasa el arreglo arr

void fun(int l, int m, int n, int arr[ ][DOS][CUATRO]){ } // define fun( )
Se pierde parametrización en las dimensiones.
3 Definir estrucuras complejas: colas, grafos, redes, etc. o estructuras definidas por el desarrollador Apuntadores
4 implementar estructuras lógicas complejas en la RAM lineal Apuntadores
5 El personal de Soporte Técnico configura (set up) los tamaños de RAM, en modo balanceado para: Memoria dinámica
Programas: que deben ser pequeños, en lo posible
Area común de datos: que debe ser grande, en lo posible

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?

2) ¿Que librería se requiere para utilizar la memoria dinámica?

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?

4) El siguiente código es correcto:


int n, *p;
for(n=1, p=NULL; ; n++){ // ¿por qué se asigna p=NULL?

p = (int *)realloc(n*sizeof(int)); // ¿por qué: se usa el casting (int *) y se multiplica por sizeof(int)?
… // ¿Qué se hace acá?

09.09 Ejercicios de Programación


1) Lea dos enteros positivos m y n, aloje espacio dinámico para A[m][n] cárguele datos e imprímalos, aloje espacio para B = trans-
puesta de A e imprímala.

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

También podría gustarte