El documento explica los conceptos de memoria estática y dinámica en C++. La memoria estática se reserva de forma fija en tiempo de compilación, mientras que la memoria dinámica permite cambiar el tamaño de los arreglos en tiempo de ejecución usando los operadores new y delete. Se describen ejemplos de cómo crear y usar arreglos estáticos y dinámicos, así como posibles errores de memoria como fugas y acceso a memoria no asignada.
2. ¿Qué es la memoria?
• Es un lugar donde se guarda información a
usar.
• Cuando declaramos una variable o un arreglo,
el programa indica que se debe reservar un
espacio de memoria (la cantidad depende del
tipo de dato) desde antes de compilar
• Esta memoria permanecerá apartada para ser
usada durante el tiempo que el programa lo
requiera
9. • Sabemos que podemos hacer un arreglo tan
grande como queramos.
• ¿Recuerdas cómo crear, llenar y mostrar un
arreglo en C++?
• Hagamos un ejemplo en Visual Studio…
10. Si lo hiciéramos como procedimiento
definido por el usuario…
void arregloEstatico(void){
int aE[TAM]; //aquí se declara el arreglo estático cuyo tamaño fijo
//está definido por la constante TAM
cout << "nARREGLO ESTATICOn";
//ciclo para llenar el arreglo estático
for (int i = 0; i < TAM; i++){
cout << "nEscribe elemento " << i << " del arreglo: ";
cin >> aE[i];
}
cout << "n";//imprime en pantalla una línea en blanco
//ciclo para mostrar arreglo estático
for (int i = 0; i < TAM; i++)
cout << aE[i] << " ";
cout << "n";//imprime en pantalla una línea en blanco
}//fin procedimiento arregloEstatico
11. Pero …
• ¿Qué pasa si queremos cambiar el tamaño del
arreglo durante ejecución?
• C++ permite cambiar la memoria según se va
necesitando
• A esto se llama “memoria dinámica”
• Las ventajas de la memoria dinámica es que se
aprovecha mejor la memoria de la máquina,
apartando solamente lo que se necesita
12. Operadores “new” y “delete”
• new() permite reservar memoria de almacén
libre
• delete() permite librear la memoria cuando ya
no se necesita
13. NEW
• Permite reservar un bloque de memoria y
devuelve la dirección de comienzo de dicho
bloque
<variable_apuntador> = new <tipo_dato>;
char *p = NULL;
p= new char;
14. NEW
• Con este operador también se pueden
reservar bloques de memoria para arreglos.
• Estos se conocen como arreglos dinámicos.
• Si el operador “new” falla, devuelve un nulo,
por lo que es obligatorio comprobar que el
compilador ha podido realizar la reserva de
memoria.
15. Ejemplo práctico
• Ahora realicemos un arreglo dinámico en C++
usando Visual Studio…
16. • Si lo escribiéramos como un procedimiento
definido por el usuario, quedaría…
17. void arregloDinamico(void){
int tamDin = 0; //variable para que el usuario indique el tamaño del arreglo que quiere
cout << "nARREGLO DINAMICOn";
cout << "Escribe el tamaño del arreglo de enteros: ";//pide a usuario tamaño deseado
cin >> tamDin; //se guarda el tamaño en variable tamDin
int *pArregloDin; //apuntador para guardar dirección de inicio del arreglo dinámico
pArregloDin = new int[tamDin]; //se aparta memoria dinámica para el arreglo pArregloDin
//ciclo para llenar los elementos del arreglo dinámico
for (int i = 0; i < tamDin; i++){
cout << "nElemento " << i << " ";//pide al usuario ingrese cada uno de
//los elementos del arreglo
cin >> pArregloDin[i];
}//fin for
cout << "n";//imprime en pantalla una línea en blanco
//ciclo para mostrar arreglo dinámico
for (int i = 0; i < tamDin; i++)
cout << pArregloDin[i] << " "; //fin for
cout << "n";//imprime en pantalla una línea en blanco
delete pArregloDin; //librea la memoria dinámica reservada para el arreglo
}//fin procedimiento arreglo dinámico
18. La estructura de un programa que use
ambos procedimientos mostrados es:
#include <iostream>
using namespace std;
#define TAM 10
void arregloEstatico(void);
void arregloDinamico(void);
int main(){
//arreglo estático
arregloEstatico();
//arreglo dinámico
arregloDinamico();
system("pause");
return 0;
}//fin main
20. • La corrupción de memoria ocurre en un
programa cuando los contenidos de una
dirección de memoria se modifican
involuntariamente debido a errores de
programación.
21. • Cuando los contenidos corruptos de memoria
se usan más adelante en el programa, llevan a
un error o comportamiento extraño del
mismo.
22. • La utilización de apuntadores que dan acceso
explícito a direcciones de memoria y
aritmética de apuntadores, que permiten
desarrollar aplicaciones eficientes, si no se
usan de forma adecuada pueden ocasionar
errores de corrupción de memoria.
23. • Estos errores son los más peligrosos y los más
difíciles de detectar debido a:
– El origen de la corrupción de memoria y su
manifestación durante la ejecución del programa,
pueden estar muy separados, por lo que es difícil
relacionar la causa y el efecto.
– Los “síntomas” aparecen en condiciones
inusuales, por lo que es difícil reproducir el error
de manera consistente o constante.
24. Categorías de errores
1. Utilización de memoria no inicializada: se
considera que los contenidos de la memoria
no inicializada son valores basura (como
poner comida recién hecha en un plato
sucio), y usar estos valores puede llevar al
comportamiento imprevisible del programa.
25. Categorías de errores (cont.)
2. Uso de memoria sin dueño: Cuando un
apuntador es nulo o apunta a una sección de
memoria ya liberada, o una ubicación de
memoria que esta fuera de la pila de
memoria del programa. Suelen causar
excepciones de “fallos de página”, lo que
lleva a una falla del programa.
26. Categorías de errores (cont.)
3. Usar memoria más allá de la asignada (buffer
overflow): Si se tiene un arreglo que es usado
dentro de un bucle/ciclo que tiene sus
contadores de elementos incorrectos, la
memoria más allá de los límites del arreglo
puede ser manipulada.
27. Categorías de errores (cont.)
4. Gestión defectuosa de memoria heap: Fugas
de memoria y liberar memora que no es del
“heap” o está sin asignar.
28. Cómo evitar fugas de memoria
• Una fuga de memoria es un error de
programación que ocurre cuando un
programa no libera toda la memoria que se
reservó, y debido a esto la aplicación puede
quedarse sin memoria y causar que el sistema
falle.
• Para prevenir esto se debe saber cuándo
ocurren y hacer buen uso de los operadores
“new” y “delete”.
29. Cómo evitar fugas de memoria
1. Por cada “new” se debe utilizar un “delete”
para liberar la misma cantidad de memoria
reservada con “new”.
2. Reserva memoria sólo si la has borrado.
char * str = new char[30]; //aparta dirección mem
//delete [] str; //esta linea corrige error
str = new char [60]; //da otra dirección de mem
//se pierde la primera memoria apartada
3. Vigila las asignaciones de apuntadores.
30. Tarea MRR
• Investiga qué es el “heap” de memoria y
escríbelo en tu cuaderno MRR
• Investiga códigos en C++ que ejemplifiquen los
tipos de errores de manejo de memoria
mencionados aquí.
• Describe cada ejemplo que investigaste,
detallándolo por escrito en tu cuaderno MRR.