Metodologa de la Programacion I
Punteros
y
Gestion Dinamica de Memoria
Objetivos
Conocer y manejar el tipo de dato puntero.
Conocer la estructura y la gestion de la memoria
de un ordenador desde el punto de vista de un
programador de C++.
Usar apropiadamente los operadores de reserva y
liberacion de memoria dinamica.
Iniciar a los alumnos en el desarrollo de tipos de
datos que requieren manejo de memoria dinamica.
Gestion de Memoria Dinamica 2
2.1 El tipo de dato puntero
Un puntero es un tipo de dato que contiene la
direccion de memoria de un dato, incluyendo una
direccion especial llamada direccion nula, repre-
sentada en C++ por el valor 0. En C la direccion
nula se suele representar por la constante NULL
(definida en <stdlib.h> y en <stddef.h> entre
otros).
Declaracion de datos de tipo puntero.
Un dato de tipo puntero se debe declarar como
cualquier otro dato.
El formato es el siguiente:
<tipo> *<nombre>
donde:
<nombre> es el nombre de la variable puntero.
<tipo> es el tipo del objeto cuya direccion de
memoria contiene <nombre>.
Gestion de Memoria Dinamica 3
Ejemplo: Declaracion de punteros
Cdigo Representacion grafica
de la memoria
1011 ...
1007 ? ptrc
char c = a;
char *ptrc; 1003 ? ptri
int *ptri;
1002 a c
1001 ...
declara
c como un variable de tipo caracter cuyo valor es
a,
ptri como una variable de tipo puntero que puede
contener direcciones de memoria de objetos de
tipo int
ptrc como una variable puntero que puede con-
tener direcciones de memoria de objetos de tipo
char.
Gestion de Memoria Dinamica 4
Se dice que
ptri es un puntero a enteros
ptrc es un puntero a caracteres.
Nota 1: Cuando se declara un puntero se reserva
memoria para albergar la direccion de memoria de
un dato, no el dato en s.
Nota 2: El tamano de memoria reservado para
albergar un puntero es el mismo (usualmente 32 bits)
independientemente del tipo de dato al que apunte.
Gestion de Memoria Dinamica 5
2.1.1 Operaciones basicas con punteros
Los operadores basicos para trabajar con punteros
son dos:
Operador de direccion &
&<variable> devuelve la direccion de memoria
donde empieza la variable <variable>.
El operador & se utiliza habitualmente para asignar
valores a datos de tipo puntero.
int i, *ptri;
ptri = &i;
i es una variable de tipo entero, por lo que la
expresion &i es la direccion de memoria donde
comienza un entero y, por tanto, puede ser asig-
nada al puntero ptri.
Se dice que ptri apunta o referencia a i.
Gestion de Memoria Dinamica 6
Operador de indireccion *
*<puntero> devuelve el contenido del objeto ref-
erenciado por <puntero>.
Esta operacion se usa para acceder al objeto ref-
erenciado o apuntado por el puntero.
char c, *ptrc;
ptrc = &c;
*ptrc = A; // equivalente a c = A
ptrc es un puntero a caracter que contiene la
direccion de c, por tanto, la expresion *ptrc es
el objeto apuntado por el puntero, es decir, c.
Un puntero contiene una direccion de memo-
ria y se puede interpretar como un numero en-
tero aunque un puntero no es un numero en-
tero. No obstante existen un conjunto de oper-
adores que se pueden realizar sobre un puntero:
+,-,++,--, !=, ==
Gestion de Memoria Dinamica 7
Ejemplo: Ejemplo de uso de punteros
int main() {
char y = 5, z = 3;
char *nptr;
char *mptr;
nptr = &y;
z = *nptr;
*nptr = 7;
mptr = nptr;
mptr = &z;
*mptr = *nptr;
y = (*mptr) + 1;
}
Gestion de Memoria Dinamica 8
Existe una gran relacion entre punteros y vectores.
Al declarar un vector
<tipo> <identif>[<n_elem>]
realmente:
1. Se reserva memoria para almacenar <n_elem>
elementos de tipo <tipo>.
2. Se crea un puntero CONSTANTE llamado
<identif> que apunta a la primera posicion
de la memoria reservada.
Por tanto, el identificador de un vector, es un
puntero CONSTANTE a la direccion de memoria
que contiene el primer elemento. Es decir, v es
igual a &v[0].
Gestion de Memoria Dinamica 9
Podemos, pues, hacer:
int v[3];
int *ptr;
...
ptr = &v[0]; // ptr = v
v[0]
v6 7 5
>
ptr
v[0]=6 es equivalente a *v=6 y a *(&v[0])=6
Por la misma razon, a los punteros se les pueden
poner subndices y utilizarlos como si fuesen vec-
tores.
v[i] es equivalente a ptr[i]
Gestion de Memoria Dinamica 10
2.1.2 Errores mas comunes
Asignar punteros de distinto tipo:
int a = 10, *ptri;
float b = 5.0, *ptrf;
ptri = &a;
ptrf = &b;
ptrf = ptri; // ERROR
Uso de punteros no inicializados:
char y = 5, *nptr;
*nptr = 5; // ERROR
Asignacion de valores al puntero y no a la variable
a la que apunta:
char y = 5, *nptr = &y;
nptr = 9; // ERROR
Gestion de Memoria Dinamica 11
2.1.3 Punteros a punteros
Un puntero a puntero es un puntero que contiene
la direccion de memoria de otro puntero.
Ejemplo: Punteros a punteros
int a = 5;
int *p;
int **q;
p = &a;
q = &p;
En este caso, para acceder al valor de la variable
a tenemos tres opciones:
a (la forma usual)
*p (a traves del puntero p)
**q (a traves del puntero a puntero q) equiv-
alente a *(*q)
Gestion de Memoria Dinamica 12
2.2 Punteros y cadenas-C
En C estandar, una cadena de caracteres se
define como un vector de tipo caracter. Para con-
trolar el final de la cadena, se utiliza el caracter
especial (\0) denominado caracter fin de cade-
na o caracter nulo.
En C++, se puede usar el tipo string para mane-
jar cadenas. Para distinguirlos, a las primeras las
denominamos cadenas-C o cstring
Una cadena es un vector de tipo char. Por ejemplo,
char cad1[10];
donde
cad1 es un vector de caracteres que puede alma-
cenar una cadena de hasta 9 caracteres (el ultimo
debe ser el terminador de cadena).
Gestion de Memoria Dinamica 13
As una nueva forma de iniciar los valores de un
vector de char es
char cad2[]="Pepe", cad3[20]="lluvia";
cad2 es un vector de caracteres de tamano 5 que
almacena la cadena Pepe y
cad3 es un vector de tamano 20, que tiene la
cadena lluvia, el caracter terminador de la cadena
y 12 posiciones no utilizadas.
Las cadenas Pepe, lluvia son de tipos const
char[5] y const char[7], respectivamente.
A causa de la fuerte relacion entre vectores y
punteros, podemos manejar las cadenas con el
tipo char *. De hecho, en la pratica se suele
utilizar mas esta notacion.
Gestion de Memoria Dinamica 14
2.2.1 Escritura y lectura de cadenas-C
Para escribir y leer una cadena de caracteres,
podemos usar cin y cout como con cualquier otro
tipo de dato basico. Por ejemplo
char cs1[80];
cout << "Introduce un nombre: ";
cin >> cs1;
cout << cs1;
La sentencia de lectura lee caracteres hasta en-
contrar un separador (espacio, tabulador, salto de
lnea).
Gestion de Memoria Dinamica 15
Para leer una lnea completa (todos los caracteres
hasta el siguiente salto de lnea), se puede usar
char cs1[80];
do{
[Link](cs1,80);
}while (*csi!=\0);
donde los parametros corresponden a un vector de
caracteres y el tamano mnimo reservado, respec-
tivamente. Se introduce dentro de un do-while
para evitar introducir lneas vacas.
Gestion de Memoria Dinamica 16
2.2.2 Funciones para cadenas-C
En lenguaje C se dispone de un conjunto de
cadenas para facilitar el uso de las cadenas-C. En
C++, se debe incluir <cstring>. Algunas son
Longitud de una cadena.
int strlen (const char *s)
Aunque indicamos int, en realidad es size t.
Asignar una cadena.
char *strcpy (char *dst, const char *src)
concatena src en dst y devuelve dst.
comparar dos cadenas.
int strcmp (const char *c1, const char *c2
que devuelve positivo si la primera mayor, negativo
si la segunda mayor y cero si iguales.
Gestion de Memoria Dinamica 17
2.3 Estructura de la memoria
Memoria estatica Segmento
de cdigo
Reserva antes de Memoria
la ejecucion del Esttica
programa Heap
Montn
Permanece fija
Espacio Libre
No requiere ges-
tion durante la Pila
ejecucion Stack
El sistema opera-
tivo se encarga de
la reserva, recu-
peracion y reuti-
lizacion.
Variables globales
y static.
Gestion de Memoria Dinamica 18
2.3.1 La pila (Stack)
Es una zona de memoria que gestiona las llamadas
a funciones durante la ejecucion de un programa.
Cada vez que se realiza una llamada a una funcion
en el programa, se crea un entorno de programa,
que se libera cuando acaba su ejecucion.
El entorno de programa almacena, entre otros:
La siguiente instruccion que se ejecutara cuando
se resuelva la llamada a la funcion (direccion de
retorno).
Los argumentos y variables locales definidos en
la funcion.
La reserva y liberacion de la memoria la realiza el
S.O. de forma automatica durante la ejecucion del
programa.
Las variables locales no son variables estaticas. Son
un tipo especial de variables dinamicas, conocidas
como variables automaticas.
Gestion de Memoria Dinamica 19
2.3.2 El monton (Heap)
Es una zona de memoria donde se reservan y
se liberan trozos durante la ejecucion de los
programas segun sus propias necesidades.
Esta memoria surge de la necesidad de los pro-
gramas de crear nuevas variables en tiempo de
ejecucion con el fin de optimizar el almacenamien-
to de datos.
Ejemplo 1: Supongamos que se desea realizar un
programa que permita trabajar con una lista de
datos relativos a una persona.
struct Persona{
string nombre;
int DNI;
image foto;
};
Gestion de Memoria Dinamica 20
A priori, es necesario definir un tamano apropiado
para almacenar un vector de Persona. Supong-
amos que realizamos la siguiente definicion:
Persona VectorPersona[100];
Que inconvenientes tiene esta definicion?
En cada ejecucion se puede requerir un numero
diferente de posiciones del vector.
Si el numero de posiciones usadas es mucho menor
que 100, tenemos reservada memoria que no va-
mos a utilizar.
Si el numero de posiciones usadas es mayor que
100, el programa no funcionara correctamente, ya
que se usan posiciones de memorias que pueden
estar usando otras variables del programa.
Solucion: Ampliar la dimension del vector y
volver a compilar.
Gestion de Memoria Dinamica 21
La utilizacion de variables estaticas o automaticas
para almacenar informacion cuyo tamano no es
conocido a priori (solo se conoce exactamente en
tiempo de ejecucion) resta generalidad al progra-
ma.
La alternativa valida para solucionar estos pro-
blemas consiste en la posibilidad de, en tiempo
de ejecucion: pedir la memoria necesaria para
almacenar la informacion y de liberarla cuando ya
no sea necesaria.
Esta memoria se reserva en el Heap y, habi-
tualmente, se habla de variables dinamicas para
referirse a los bloques de memoria del Heap que
se reservan y liberan en tiempo de ejecucion.
Gestion de Memoria Dinamica 22
2.4 Gestion dinamica de la memoria
El sistema operativo es el encargado de controlar
la memoria que queda libre en el sistema.
p1 Memoria
Esttica
(2)
Sistema
Espacio
(3) Libre
crea_vector(...)
(1) p2
main()
Pila
p3
(1) Peticion al S.O. (tamano)
(2) El S.O. comprueba si hay suficiente espacio
libre.
(3) Si hay espacio suficiente, devuelve la ubicacion
donde se encuentra la memoria reservada, y marca
dicha zona como memoria ocupada.
Gestion de Memoria Dinamica 23
2.4.1 Reserva de memoria
(4) p1 Memoria
Esttica
Heap
Sistema
Espacio
Libre
(4)
crea_vector(...)
p2
main()
Pila
p3
(4) La ubicacion de la zona de memoria se al-
macena en una variable estatica (p1) o en una
variable automatica (p2).
Por tanto, si la peticion devuelve una direccion
de memoria, p1 y p2 deben ser variables de tipo
puntero al tipo de dato que se ha reservado.
Gestion de Memoria Dinamica 24
p1 Memoria
Esttica
Heap
Sistema
(5)
Espacio
Libre
crea_vector(...)
p2
main()
Pila
p3
(5) A su vez, es posible que las nuevas variables
dinamicas creadas puedan almacenar la direccion
de nuevas peticiones de reserva de memoria.
Gestion de Memoria Dinamica 25
2.4.2 Liberacion de memoria
p1 Memoria
Esttica
Heap
Sistema
(6)
Espacio
Libre
crea_vector(...)
p2
main()
Pila
p3
(6) Finalmente, una vez que se han utilizado las
variables dinamicas y ya no se van a necesitar mas
es necesario liberar la memoria que se esta utilizan-
do e informar al S.O. que esta zona de memoria
vuelve a estar libre para su utilizacion.
Gestion de Memoria Dinamica 26
RECORDAR LA METODOLOGIA !
1. Reservar memoria.
2. Utilizar memoria reservada.
3. Liberar memoria reservada.
Gestion de Memoria Dinamica 27
2.4.3 Objetos dinamicos simples
El operador new
new reserva una zona de memoria en el Heap
del tamano adecuado para almacenar un dato
del tipo tipo (sizeof(tipo) bytes), devolviendo
la direccion de memoria donde empieza la zona
reservada.
Si new no puede reservar espacio (p.e. no hay
suficiente memoria disponible), se provoca una
excepcion y el programa termina. El manejo de
excepciones se vera en otras asignaturas.
Gestion de Memoria Dinamica 28
Ejemplo:
(1) int *p,
q = 10;
(2) p = new int;
(3) *p = q;
Notas:
1. Observar que p se declara como un puntero mas.
2. Se pide memoria en el Heap para guardar un dato
int. Si hay espacio para satisfacer la peticion, p
apuntara al principio de la zona reservada por new.
Asumiremos que siempre hay memoria libre para
asignar.
3. Se trabaja como ya sabemos con el objeto refe-
renciado por p.
Gestion de Memoria Dinamica 29
El operador delete
delete puntero;
delete permite liberar la memoria del Heap que
previamente se haba reservado y que se encuentra
referenciada por un puntero.
Ejemplo:
(1) int *p,
q = 10;
(2) p = new int;
(3) *p = q;
......
(4) delete p;
Notas:
4. El objeto referenciado por p deja de ser opera-
tivo y la memoria que ocupaba esta disponible
para nuevas peticiones con new.
Gestion de Memoria Dinamica 30
2.4.4 Objetos dinamicos compuestos
Para el caso de objetos compuestos (p.e. struct)
la metodologa a seguir es la misma, aunque te-
niendo en cuenta las especificidades de los tipos
compuestos.
En el caso de los struct, la instruccion new
reserva la memoria necesaria para almacenar todos
y cada uno de los campos de la estructura.
struct Persona{
string nombre;
string DNI;
};
Persona *yo;
yo = new Persona;
cin >> (*yo).nombre;
cin >> (*yo).DNI;
......
delete yo;
Gestion de Memoria Dinamica 31
El operador -> de registros
En el tema anterior se vio que existan dos ope-
radores para el acceso a los campos de un registro:
el operador punto (.) y el operador flecha (->).
El operador flecha se utiliza para el acceso a los
campos de un registro que se encuentra apuntado
por un puntero.
Veamos el siguiente ejemplo:
struct Persona{
string nombre;
string DNI;
};
Persona yo, // variable a tipo Persona.
*clon; // puntero a tipo Persona.
clon = &yo;
yo->nombre="Pepe" // Error
clon->nombre = "Pepe" // Correcto
(*clon).nombre="Pepe" // Equivalente
Para usar correctamente el operador flecha, la
variable que se encuentra a la izquierda de la ->
debe ser de tipo puntero a la estructura.
Gestion de Memoria Dinamica 32
Un ejemplo mas completo
Dada la definicion del siguiente tipo de dato
Persona y declaracion de variable
struct Persona {
string nombre;
Persona *amigos;
};
Persona *yo;
V. Automticas V. Dinmicas
yo ?
Gestion de Memoria Dinamica 33
Que realiza la siguiente secuencia de instruc-
ciones?
1. yo = new Persona;
V. Automticas V. Dinmicas
yo ?
Reserva memoria para almacenar (en el Heap) un
dato de tipo Persona. Como es un tipo com-
puesto, realmente se reserva espacio para cada
uno de los campos que componen la estructura,
en este caso, un string y un puntero.
Gestion de Memoria Dinamica 34
2. yo->nombre = "Manolito";
V. Automticas V. Dinmicas
yo "Manolito" ?
Asigna un valor al campo nombre del nuevo objeto
dinamico creado.
Como la referencia a la variable se realiza mediante
un puntero, puede utilizarse el operador flecha (->)
para el acceso a los campos de un registro.
Gestion de Memoria Dinamica 35
3. yo->amigos = new Persona;
Reserva memoria para almacenar (en el Heap) otro
dato de tipo Persona, que es referenciada por el
campo amigos de la variable apuntada por yo
(creada anteriormente).
V. Automticas V. Dinmicas
yo "Manolito"
Por tanto, a partir de una variable dinamica se
pueden definir nuevas variables dinamicas siguien-
do una filosofa semejante a la propuesta en el
ejemplo.
Gestion de Memoria Dinamica 36
4. Persona un_amigo = *yo;
Se crea la variable automatica un amigo y se
realiza una copia de la variable que es apuntada
por yo.
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" ?
5. Persona *p = yo->amigos;
La variable p almacena la misma direccion de
memoria que el campo amigos de la variable
apuntada por yo.
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" ?
Gestion de Memoria Dinamica 37
6. p->nombre = "Pepe";
Usando la variable p (apunta al ultimo dato crea-
do) damos valor al campo nombre.
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" "Pepe" ?
7. p->amigos = &un_amigo;
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" "Pepe"
Es posible hacer que una variable dinamica apunte
a una variable automatica o estatica usando el
operador &.
Gestion de Memoria Dinamica 38
8. yo = p;
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" "Pepe"
Con este orden se pierde el acceso a uno de
los objetos dinamicos creados, siendo imposible
su recuperacion. Por tanto, antes de realizar una
operacion de este tipo, hay que asegurar:
a) que no perdemos la referencia a ese objeto (ex-
iste otro puntero que lo referencia). Esto se
debe considerar cuando el objeto dinamico sigue
siendo util para el programa.
b) Si la variable ya no es util para el programa,
debemos liberar antes la memoria (indicando al
sistema que esa zona puede ser utilizada para
almacenar otros datos).
Gestion de Memoria Dinamica 39
Volvamos a la situacion anterior
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito" "Pepe"
9. delete un_amigo.amigos;
Esta sentencia libera la memoria cuya direccion
de memoria se encuentra almacenada en el campo
amigos de la variable un amigo.
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito"
Gestion de Memoria Dinamica 40
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito"
La liberacion implica que la zona de memoria
queda disponible para que otro programa (o el
mismo) pudieran volver a reservarla. Sin embargo,
la direccion que almacenaba el puntero usado para
la liberacion (y el resto de punteros) se mantiene
tras la liberacion.
Por consiguiente, hay que tener cuidado y no
usar la direccion almacenada en un puntero
que ha liberado la memoria. Por ejemplo:
un_amigo.amigos->nombre = "Alex";
De igual forma, hay que tener cuidado con todos
aquellos apuntadores que mantenian la direccion
de una zona liberada ya que se encuentran con el
mismo problema.
yo->amigos->nombre = "Alex";
Gestion de Memoria Dinamica 41
Una forma de advertir esta situacion es asignar
la direccion nula a todos aquellos punteros que
apunten a zonas de memoria que ya no existen.
10. yo->amigos = un_amigo.amigos = p = 0;
V. Automticas V. Dinmicas
yo "Manolito"
un_amigo
"Manolito"
11. delete yo;
V. Automticas V. Dinmicas
yo
un_amigo
"Manolito"
Gestion de Memoria Dinamica 42
2.4.5 Vectores dinamicos
Hasta ahora solo podemos crear un vector cono-
ciendo a priori el numero mnimo de elementos
que podra tener. P.e. int vector[22];
Esa memoria esta ocupada durante la ejecucion
del modulo en el que se realiza la declaracion.
Para reservar la memoria estrictamente necesaria:
El operador new []
......
tipo * p;
p = new tipo[ num ];
Reserva una zona de memoria en el Heap para
almacenar num datos de tipo tipo, devolviendo
la direccion de memoria inicial.
Gestion de Memoria Dinamica 43
La liberacion se realiza con delete []
delete [] puntero;
que libera (pone como disponible) la zona de
memoria previamente reservada por una orden
new [] y que esta referencia por un punetro.
Con la utilizacion de de esta forma de reserva
dinamica podemos crear vectores que tengan justo
el tamano necesario. Podemos, ademas, crearlo
justo en el momento en el que lo necesitamos y
destruirlo cuando deje de ser util.
Ejemplo:
#include <iostream>
using namespace std;
int main()
{
int *v, n;
cout << "Numero de casillas: ";
cin >> n;
// Reserva de memoria
v = new int [n];
Gestion de Memoria Dinamica 44
// Procesamiento del vector dinamico:
// lectura y escritura de su contenido
for (int i= 0; i<n; i++) {
cout << "Valor en casilla "<<i<< ": ";
cin >> v[i];
}
cout << endl;
for (int i= 0; i<n; i++)
cout << "En la casilla " << i
<< " guardo: "<< v[i] << endl;
// Liberar memoria
delete [] v;
Gestion de Memoria Dinamica 45
Una funcion puede devolver un vector dinamico
como copia de otro que recibe como argumento.
int * copia_vector1 (int v[], int n)
{
int * copia = new int [n];
for (int i= 0; i<n; i++)
copia[i] = v[i];
}
return (copia);
}
El vector devuelto tendra que ser, cuando deje de
ser necesario, liberado con delete []:
int * v1_copia;
......
v1_copia = copia_vector1 (v1, 500);
// Usamos v1_copia
...
delete [] v1_copia;
Gestion de Memoria Dinamica 46
Un error muy comun a la hora de construir una
funcion que copie un vector es el siguiente:
int *copia_vector1(const int v[], int n)
{
int copia[100]; // o cualquier otro valor
// mayor que n
for (int i= 0; i<n; i++)
copia[i] = v[i];
return (copia);
}
Al ser copia una variable local no puede ser
usada fuera del ambito de la funcion en la que
esta definida.
Gestion de Memoria Dinamica 47
Ejemplo: ampliacion del espacio ocupado por un
vector dinamico.
void AmpliarVector (int * &v, int nelems,
int nadicional)
{
int v_ampliado = new int[nelems + nadicional];
for (int i= 0; i<nelems; i++)
v_ampliado[i] = v[i];
delete [] v;
v = v_ampliado;
}
Cuestiones a tener en cuenta:
v se pasa por referencia porque se va a modificar
el lugar al que apunta.
Es necesario liberar v antes de asignarle el mis-
mo valor de v_copia ya que si no perderamos
cualquier referencia a ese espacio de memoria.
Poco eficiente. Probar con memcpy().
Gestion de Memoria Dinamica 48
Ejemplo: Registros y cadenas de caracteres
struct Persona{
char *nombre;
char NIF[10]; //8digit+letra+\0
};
Persona yo;
char aux[120];
// Leer datos
cout << "Introduzca nombre: ";
do {
[Link](aux,120);
}while (strlen(aux) == 0);
[Link] = new char[strlen(aux)];
strcpy([Link],aux);
cout << "Introduzca NIF: ";
do{
[Link]([Link]);
}while (strlen([Link]) == 0);
Gestion de Memoria Dinamica 49
2.4.6 Matrices dinamicas
Problema:
Gestionar matrices 2D de forma dinamica, en
tiempo de ejecucion.
Motivacion:
El lenguaje proporciona matrices estaticas para las
que debe conocerse el tamano en tiempo de com-
pilacion. En el caso de matrices 2D, el compilador
debe conocer el numero de filas y columnas.
Necesitamos poder crear y trabajar con matrices
2D cuyo tamano (filas y columnas) sea exacta-
mente el que requiera el problema a resolver.
En particular, posibilitaremos:
Creacion de matrices dinamicas.
Destruccion de matrices dinamicas.
Acceso mediante ndices.
Gestion de Memoria Dinamica 50
Aproximaciones:
1. Datos guardados en filas independientes.
2. Datos guardados en una unica fila.
Solucion 1
matriz
0 1 2 3 4 cols1
1
0 1 2 3 4 cols1
3
fils1
0 1 2 3 4 cols1
int ** Matriz2D_1 (int fils, int cols);
void LiberaMatriz2D_1 (int **matriz,
int fils, int cols);
Gestion de Memoria Dinamica 51
Creacion (1)
int ** Matriz2D_1 (int fils, int cols)
{
bool error = false;
int ** matriz;
int f, i;
// "matriz" apunta a un vect. de punt. a filas
matriz = new int * [fils];
for (f=0; f<fils; f++)
// "matriz[f]" apuntara a un vector de int*
matriz[f] = new int [cols];
return (matriz);
}
Liberacion (1)
void LiberaMatriz2D_1 (int **matriz,int fils,int cols)
{
for (int f=0; f<fils; f++)
delete [] matriz[f];
delete [] matriz;
}
Gestion de Memoria Dinamica 52
Solucion 2
1a fila 2a fila nesima fila
matriz
0 1 cols1 2 x cols1 fils*cols1
1
fils1
Las funciones especficas para esta representacion
tendran el mismo interfaz que las de la solucion
anterior:
int ** Matriz2D_2 (int fils, int cols);
void LiberaMatriz2D_2 (int **matriz,
int fils, int cols);
Gestion de Memoria Dinamica 53
Creacion (2)
int ** Matriz2D_2 (int fils, int cols)
{
int ** matriz;
int f;
// "matriz" apunta a un vect. de punt.
// que apuntaran al inicio de cada fila
matriz = new int * [fils];
matriz[0] = new int [fils*cols];
for(f=1; f<fils ; f++)
matriz[f] = matriz[f-1] + cols;
}
return (matriz);
}
Gestion de Memoria Dinamica 54
Liberacion (2)
void LiberaMatriz2D_2 (int **matriz,
int fils, int cols)
{
delete [] matriz[0];
delete [] matriz;
}
Ejemplo de utilizacion
#include <iostream>
#include <iomanip>
using namespace std;
void LeeDimensiones (int &num_filas, int &num_cols);
int ** Matriz2D_1 (int fils, int cols);
int ** Matriz2D_2 (int fils, int cols);
void PintaMatriz2D (int **matriz, int fils, int cols)
void LiberaMatriz2D_1 (int **matriz,
int fils, int cols);
void LiberaMatriz2D_2 (int **matriz, int fils,
int cols);
/*****************************************************/
Gestion de Memoria Dinamica 55
/*****************************************************/
int main ()
{
int ** m1; // "m1" y "m2" seran matrices
int ** m2; // dinamicas 2D.
int filas, cols;
int f, c;
// Leer num. de filas y columnas
LeeDimensiones (filas, cols);
// Crear matrices dinamicas
cout << endl << "Creando Matriz 1 (";
cout << filas << "X"<< cols << ")" << endl;
m1 = Matriz2D_1 (filas, cols);
cout << "Creando Matriz 2 (";
cout << filas << "X"<< cols << ")" << endl;
m2 = Matriz2D_2 (filas, cols);
// Rellenarlas (observar el acceso por indices)
cout << endl << "Rellenando matrices" << endl;
Gestion de Memoria Dinamica 56
for (f=0; f<filas; f++)
for (c=0; c<cols; c++) {
m1[f][c] = ((f+1)*10)+c+1;
m2[f][c] = ((f+1)*10)+c+1;
}
// Mostrar su contenido
cout << endl << "Matriz 1:" << endl;
PintaMatriz2D (m1, filas, cols);
cout << endl << "Matriz 2:" << endl;
PintaMatriz2D (m2, filas, cols);
// Liberar la memoria ocupada
cout << endl << "Liberando matriz 1" << endl;
LiberaMatriz2D_1 (m1, filas, cols);
cout << "Liberando matriz 2" << endl << endl;
LiberaMatriz2D_2 (m2, filas, cols);
return (0);
}
/*****************************************************/
Gestion de Memoria Dinamica 57
void LeeDimensiones (int &num_filas, int &num_cols)
{
cout << "Numero de filas : ";
cin >> num_filas;
cout << "Numero de columnas : ";
cin >> num_cols;
}
/*****************************************************/
void PintaMatriz2D (int **matriz, fils, int cols)
{
for (int f=0; f<fils; f++) {
for (int c=0; c<cols; c++)
cout << setw(4) << matriz[f][c]; // Indices
cout << endl;
}
}
/*****************************************************/
// Resto de funciones: Matriz2D_1(), Matriz2D_2(),
// LiberaMatriz2D_1 y LiberaMatriz2D_2()
Gestion de Memoria Dinamica 58