• No se han encontrado resultados

301305 Estructura de Datos

N/A
N/A
Protected

Academic year: 2021

Share "301305 Estructura de Datos"

Copied!
191
0
0

Texto completo

(1)

UNIVERSIDAD NACIONAL ABIERTA Y A DISTANCIA

ESCUELA DE CIENCIAS BÁSICAS TECNOLOGÍA E INGENIERÍA PROGRAMA INGENIERÍA DE SISTEMAS

301305 – ESTRUCTURA DE DATOS HERMES MOSQUERA ANGULO

(Director Nacional)

CARLOS ROBERTO ROJAS Acreditador

POPAYAN Enero de 2011

(2)

INDICE DE CONTENIDO

UNIDAD 1. MEMORIA DINÁMICA………..………..13

CAPITULO 1: APUNTADORES ... 15

Lección 1: Conceptos básicos de apuntadores ... 15

Lección 2: Variables Automáticas y Apuntadores ... 18

Lección 3: Apuntadores y Cadenas ... 21

Lección 4: Paso de Arreglos Como Parámetros ... 23

Lección 5: Apuntadores a Apuntadores ... 27

CAPITULO 2: GESTIÓN DINÁMICA DE MEMORIA ... 31

Lección 6: Conceptos Básicos de Memoria ... 31

Lección 7: Tipos de datos comunes ... 32

Lección 8: Tipos de Variables ... 35

Lección 9: Variables Dinámicas ... 36

Lección 10: Asignar y Liberar Espacios de Memoria ... 37

CAPITULO 3: OPERADORES Y FUNCIONES EN LA GESTIÓN DE MEMORIA ... 40

Lección 11: Operadores New y Delete ... 40

Lección 12: Más acerca de la Implementación de New y Delete ... 44

Lección 13: Funciones Malloc() y Free() ... 47

Lección 14: Aplicación a la asignación de memoria con Malloc() y Free() ... 48

(3)

UNIDAD 2. ESTRUCTURAS DE DATOS LINEALES……….……...56

CAPITULO 4: PILAS ... 59

Lección 1: Conceptos básicos de pilas ... 59

Lección 2: Operaciones realizadas con pilas ... 60

Lección 3: Operaciones básicas con pilas paso a paso ... 62

Lección 4: Análisis del código propuesto para implementar una pila ... 70

Lección 5: Aplicación de las estructuras lineales tipo pilas ... 75

CAPITULO 5: COLAS ... 82

Lección 6: Conceptos básicos de colas ... 82

Lección 7: Operaciones básicas con colas ... 84

Lección 8: Implementación de las Colas por medio de Punteros ... 87

Lección 9: Implementación del código completo de la cola ... 90

Lección 10: Aplicación de una cola en un entorno real planificador de citas ... 93

CAPITULO 6: LISTAS ... 97

Lección 11: Concepto básicos de Listas ... 97

Lección 12: Listas enlazadas ... 99

Lección 13: Más a cerca de listas enlazadas ... 105

Lección 14: Listas doblemente enlazadas ... 109

Lección 15: Listas circulares ... 117

UNIDAD 3. ESTRUCTURAS DE DATOS NO LINEALES……….………...……..125

CAPITULO 7: ÁRBOLES ... 131

Lección 1: Teoría general de Árboles ... 131

Lección 2: Otros conceptos de la teoría general de árboles ... 132

(4)

Lección 4: Estructura para la creación de un árbol de orden tres ... 134

Lección 5. Introducción al Modo Gráfico de C++ ... 136

CAPITULO 8: ÁRBOLES BINARIOS ... 142

Lección 6: Conceptualización de Arboles Binarios... 142

Lección 7: Clasificación de los árboles binarios ... 1477

Lección 8: Formas de Recorrer un Árbol Binario ... 149

Lección 9: Ábol binario de búsqueda (ABB) ... 154

Lección 10: Operaciones en ABB ... 157

CAPITULO 3: GRAFOS... 176

Lección 11: Conceptos básicos de grafos ... 176

Lección 12: Grafo no dirigido ... 178

Lección 13: Representación de los grafos ... 1800

Lección 14: Representación mediante listas de Adyacencia ... 1833

(5)

LISTADO DE TABLAS

Tabla 1……….30 Tabla 2……….31

(6)

LISTADO DE GRÁFICOS Y FIGURAS

Figura 1 Salida en pantallade progra1.cpp ... 17

Figura 2 Salida en pantalla de progra2.cpp ... 19

Figura 3 Salida en pantalla de progra3.cpp ... 21

Figura 4 Salida en pantalla de progra4.cpp ... 22

Figura 5 Salida en pantalla de progra5.cpp ... 23

Figura 6 Salida en pantalla de progra6.cpp ... 25

Figura 7 Salida en pantalla de progra7.cpp ... 26

Figura 8 Salida en pantalla de progra8.cpp ... 27

Figura 9 Salida en pantalla de progra9.cpp ... 28

Figura 10 Salida en pantalla de progra10.cpp ... 30

Figura 11 Salida en pantalla de progra11.cpp ... 34

Figura 12 Salida en pantalla de progra12.cpp ... 36

Figura 13Salida en pantalla de progra13.cpp ... 37

Figura 14 Salida en pantalla de progra14.cpp ... 39

Figura 15 Salida en pantalla de progra15.cpp ... 42

Figura 16 Salida en pantalla de progra16.cpp ... 45

Figura 17 Salida en pantalla de progra17.cpp ... 46

Figura 18 Salida en pantalla de progra18.cpp ... 48

Figura 19 Salida en pantalla de progra19.cpp ... 50

Figura 20 Representación gráfica de una pila de monedas ... 60

(7)

Figura 22 Declaración de la estructura pila ... 64

Figura 23 Menú de opciones de la pila ... 66

Figura 24 Definición de la función insertar() ... 66

Figura 25 La función insertar() en el case 1 ... 67

Figura 26 Instrucciones de la función insertar() ... 68

Figura 27 Vista de la definición de las funciones insertar, visualizar y extraer ... 70

Figura 28 Salida en pantalla de Progra21.cpp ... 75

Figura 29 Salida en pantalla de Progra22.cpp ... 78

Figura 30 Salida en pantalla de Progra23.cpp ... 81

Figura 31 Representación gráfica de una cola de personas. ... 83

Figura 32 Representación gráfica de una cola ... 84

Figura 33 Salida en pantalla de progra24... 87

Figura 34 Salida en pantalla de Progra25.cpp ... 93

Figura 35 Salida en pantalla de progra26.cpp ... 96

Figura 36 Representación gráfica de una lista contigua ... 99

Figura 37 Representación gráfica de listas enlazadas ... 100

Figura 38 Salida en pantalla de Progra27.cpp ... 105

Figura 39 Salida en pantalla de progra28.cpp ... 106

Figura 40 Salida en pantalla de progra29.cpp ... 108

Figura 41 Representación gráfica de una lista doblemente enlazada ... 109

Figura 42 Salida en pantalla de progra30.cpp ... 117

Figura 43 Representación gráfica de una Lista Circular ... 118

Figura 44 Lista circular simplemente enlazada ... 119

(8)

Figura 46 Primer nodo de la lista circular ... 120

Figura 47 Inserción del segundo nodo ... 120

Figura 48 Nuevo nodo insertado en la lista circular ... 121

Figura 49 Salida en pantalla de progra31.cpp ... 124

Figura 50 Representación gráfica de árboles ... 132

Figura 51 Interfaz gráfica del compilador Borland C++ 5.5. ... 138

Figura 52 Activación de las librerías Gráficas ... ¡Error! Marcador no definido. Figura 53 Configuración de directorio para BGI ... 139

Figura 54 Interfaz de directorios de C++. ... 139

Figura 55 Inclusión de la ruta C:\TC\BGI ... 140

Figura 56 Salida en pantalla de progra32 Modo grafico de C++. ... 141

Figura 57 Estructura de un árbol binario ... 142

Figura 58 Representación gráfica de un árbol binario ... 143

Figura 59 Representación gráfica del subárbol izquierdo... 143

Figura 60 Representación gráfica del subárbol derecho ... 144

Figura 61 Representación gráfica de un nodo padre ... 144

Figura 62 Representación gráfica de un nodo hoja ... 145

Figura 63 Camino del árbol ... 145

Figura 64 Longitud del árbol binario ... 146

Figura 65 Nivel del árbol... 147

Figura 66 Representación gráfica de un árbol binario completo. ... 147

Figura 67 Representación gráfica de Árbol binario igual ... 148

Figura 68 Representación gráfica de árboles semejantes... 149

(9)

Figura 70 Recorrido del árbol binario en preorden ... 150

Figura 71 Resultado del recorrido en preorden del árbol ... 150

Figura 72 Representación gráfica del recorrido en Inorden... 152

Figura 73 Recorrido en Postorden del árbol binario. ... 153

Figura 74 Árbol binario como aplicación a los tres recorridos ... 154

Figura 75 Borrado de un nodo rama con intercambio de nodo hoja ... 157

Figura 76 Representación gráfica de un árbol binario de búsqueda ... 158

Figura 77 Borrado de un nodo rama con intercambio de nodo rama ... 158

Figura 78 Intercambio de un nodo rama con intercambio de nodo rama ... 159

Figura 79 Salida en pantalla de progra34... 167

Figura 80 Representación gráfica de los grafos ... 177

Figura 81 Representación gráfica de un gráfo dirigido ... 178

Figura 82 Representación gráfica de un grafo no dirigido ... 178

Figura 83 Representación gráfica de grafos etiquetados ... 179

Figura 84 Representación gráfica de la longitud del camino de un grafo ... 179

Figura 85 Grafo dirigido ... 181

Figura 86 Representación gráfica de la matriz de adyacencia ... 181

Figura 87 Representación gráfica de un grafo por lista de adyacencia ... 183

(10)

ASPECTOS DE PROPIEDAD INTELECTUAL Y VERSIONAMIENTO

La versión uno del presente módulo fue diseñado en el año 2004 por el Ing. Iván Arturo López, docente de la UNAD, ubicado en el CEAD de Popayán, el Ing. López es Ingeniero de Sistemas, Especialista en Pedagogía Para el Desarrollo del Aprendizaje Autónomo y Magister en Educación y TIC’s, se ha desempeñado como Tutor y Docente de la UNAD desde el año 2000.

La versión dos del presente módulo fue desarrollada por el Ing. Hermes Mosquera Angulo en el año 2007, quien ha sido Tutor de la UNAD en el CEAD Popayán, desde el año 2006, se desempeña actualmente como Director del cuso a nivel nacional desde el año 2007.

(11)

INTRODUCCIÓN

El curso de estructura de datos cobra vital importancia en la aplicación de la programación estructurada y al cual se le puede sacar el máximo provecho, puesto que se requiere que los estudiantes que abordan el curso tengan los conocimientos previos, tratados en los cursos anteriores de Algoritmos e Introducción a la programación. Estructura de datos es un curso metodológico de compuesto de tres unidades didácticas en donde se combina la parte teórica con la parte práctica, con el desarrollo de pequeños programas de aplicación basados en entornos reales.

En ese sentido, el curso inicia con la primera unidad en la que se tratan temas como los fundamentos básicos de apuntadores, asignación dinámica de memoria al igual que los operadores y funciones para la gestión de memoria. Se propone una metodología de trabajo en la que se invita al estudiante a realizar una lectura detallada de cada lección con el propósito de profundizar en la conceptualización; posteriormente en la mayoría de temas se proponen códigos fuente completos que sirven como ejemplos de programas de aplicación a la temática tratada. Estos códigos pueden ser llevados a cualquier compilador de C++, para el curso se propone el compilador Turbo C++ 3.0 para plataformas Windows XP y el compilador Borland C++ 5.5 para plataformas Windows Vista y Windows 7, por su facilidad de uso amigabilidad y preconfiguración, para el caso de plataforma Linux puede emplearse el editor que trae por defecto para programas C++. Los compiladores pueden ser descargados directamente del sitio

https://sites.google.com/site/hhmosquera/file-cabinet.

En la segunda unidad se profundiza en temas relacionados con las estructuras de datos lineales, donde se abordan los temas como pilas, colas, listas, desarrollados con apuntadores, brindando al estudiante los fundamentos y competencias necesarias para brindar soluciones en programación a entornos reales.

Final en la tercera unidad se continúa con la profundización y aplicación de las estructura de datos no lineales se podría hablar de estructuras jerárquicas con los temas de árboles y grafos los cuales inducen al estudiante a trabajar con el modo gráfico de C++ como acercamiento a la programación orientada a objetos. En el curso se incluyen ejercicios prácticos, realizados por diferentes autores, sitios web como referencia y una amplia bibliografía externa y también existente en la biblioteca virtual de la UNAD, que sin duda llevara al estudiante a un aprendizaje autónomo y significativo.

(12)

12

UNIDAD 1

Nombre de la Unidad MEMORIA DINÁMICA

Introducción

La memoria del computador es un recurso invaluable de mucha importancia en el procesamiento de información, que requiere darle un buen uso por parte de los programadores con el fin de optimizar espacios en memoria. Por esta razón, en esta, la primera unidad del curso, se hace una introducción a conceptos básicos como apuntadores, asignación dinámica de memoria y operadores como new y delete propios de C++ y funciones como Malloc() y free(), propios de C.

Igualmente están implícitas diferentes estrategias de pensamiento de orden superior que el estudiante irá descubriendo gracias al apoyo permanente del tutor, quien en es el mediador del proceso de aprendizaje. En el transcurso de la unidad, se desarrollan actividades que buscan determinar el grado de apropiación del conocimiento, basados en los tres momentos: reconocimiento, profundización y transferencia, sin dejar al lado el desarrollo del componente práctico que tiene como finalidad afianzar los conocimientos en pro de lograr la competencia del curso.

Justificación

La memoria de la computadora es un recurso invaluable y finito en cuanto asu capacidad, con muchos usos. Para el caso de los programadores de computados, solo uno y es guardar información. Por tal razón es importante darle un buen manejo a la memoria y esto se puede hacer cuando en el desarrollo de programas informáticos, se hace una buena definición de las de variables de acuerdo al tipo de información que se requiera almacena.

Los datos significan la información con la cual trabaja un programa. En este mundo competitivo quien tiene la información tiene el poder. Ya sea que el programa esté trabajando con una lista de direcciones, monitoreando la bolsa de valores, manejando el presupuesto nacional o

(13)

13 cualquier otra situación, la información (nombres, precios de acciones, gastos) es guardada en la RAM de la computadora mientras el programa se esté ejecutando. Entra aquí el concepto de la gestión dinámica de memoria y los apuntadores, como un tipo de variables que permiten por medio de funciones y operadores asignar y liberar memoria dinámicamente en tiempo de ejecución, es decir mientras el programa está en marcha.

Profundizar en la conceptualización del tipo de datos y los bytes requeridos que se manejan en el leguaje C++ contribuye al buen desarrollo de programas informáticos.

Intencionalidades Formativas

Conocer y aplicar el funcionamiento de las estructuras de datos lineales.

Profundizar en aspectos básicos en apuntadores y la asignación dinámica de memoria.

Diferenciar Conceptualmente el uso y la aplicación de las variables estáticas y las variables dinámicas.

Aplicar la conceptualización de los operadores y funciones para el manejo de la memoria dinámica.

Realizar aplicaciones en lenguaje C++ utilizando estructuras dinámicas de memoria

Capítulo 1 APUNTADORES

Lección 1 Conceptos básicos de apuntadores Lección 2 Variables Automáticas y Apuntadores Lección 3 Apuntadores y Cadenas

Lección 4 Paso de Arreglos Como Parámetros Lección 5 Apuntadores a Apuntadores

Capítulo 2 GESTIÓN DINÁMICA DE MEMORIA

Lección 6 Conceptos Básicos de Memoria Lección 7 Tipos de datos comunes

Lección 8 Tipos de Variables Lección 9 Variables Dinámicas

Lección 10 Asignar y Liberar Espacios de Memoria

Capítulo 3 OPERADORES Y FUNCIONES PARA LA GESTIÓN DE MEMORIA

Lección 11 Operadores New y Delete

(14)

14 Lección 13 Funciones Malloc() y Free()

Lección 14 Aplicación a la asignación de memoria con Malloc() y Free()

(15)

15 CAPITULO 1: APUNTADORES

Introducción

En el presente capitulo se aborda la temática de los apuntadores y la aplicabilidad que tienen en el campo de la programación estructurada. Los apuntadores son un tipo especial de variables que guardan direcciones de otras variables. en los lenguajes de programación de C y C++. Proporcionan mucha utilidad al programador ya que permiten accesar y manipular datos de tal manera que no es posible realizarse en otros lenguajes llamados de alto nivel.

Tambien son utiles para pasarle parametros a las funciones de tal modo que les permiten modificar y regresar valores a la rutina o función que hace el llamado. Cuando se utilizan incorrectamente, pueden producir algunos problemas de esrtabilidad en el sistema o también pueden presentar fallas en el programa en ejecución.

De igualmanera se llevarán a cabo aplicaciones pequeñas de programas codificados en C++ como aplicación al uso de los apuntadores y a las operaciones que se pueden llevar a cabo. Cada uno de estos programas presenta en una figura la salida en pantalla, es decir todos están previamente compilados y depurados.

Lección 1: Conceptos básicos de apuntadores

Antes de definir qué es un puntero, veremos primero cómo se almacenan los datos en un computador. La memoria de un computador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes. Cada byte tiene una dirección, llamada normalmente dirección de memoria.

Definición de Apuntadores

Un apuntador es una variable, que almacena como contenido una dirección de memoria, de otra variable a la que apunta, dicha dirección representa el lugar donde se almacena un dato. Los apuntadores tienen un tipo de dato específico y

(16)

16 solo pueden apuntar a espacios de memoria con datos del mismo tipo. Por supuesto, a partir de esa dirección de memoria puede haber cualquier tipo de objeto: un char, un int, un float, un array, una estructura, una función u otro puntero. Serán los programadores los responsables de decidir ese contenido. Con los apuntadores es posible manipular estructuras de datos o asignar memoria dinámica. Los apuntadores son una de las herramientas más poderosas con que cuenta el Lenguaje C++. Desafortunadamente, muchos programadores han creado el mito de que el estudio de los apuntadores es muy complicado, lo cual ha desarrollado una fobia entre quienes se inician en el estudio de las estructuras dinámicas en lenguaje C++.

Declaración de apuntadores

Los apuntadores son variables automáticas cuyos valores representan direcciones de memoria correspondientes a otras variables del mismo tipo a las que apunta. La sintaxis para la declaración de una variable de tipo apuntador es la siguiente: tipo * identificador ; Donde, tipo hace referencia al tipo de datos de C++ como int, char, float, entre otros e identificador, hace referencia al nombre de la variable apuntador.

Ejemplo: int *apunt; se declara el apuntador apunt de tipo entero. Se dice que: "apunt va a apuntar a variables de tipo int" donde apunt es el nombre del apuntador y (*) es el operador de indirección que indica que apunt es un apuntador.

En el ejemplo anterior, puede decirse que: *apunt se refiere al objeto apuntado por apunt. apunt es un apuntador a objetos de tipo int

Observe que el operador de indirección utiliza el mismo símbolo que el operador de multiplicación. En este caso el asterisco le indica al sistema que se define una variable apuntador.

Ejemplos:

int *x; x es un apuntador de tipo entero. char *y; y es un apuntador de tipo carácter.

(17)

17 Operadores para trabajar apuntadores

Los operadores utilizados para trabajar variables apuntadores son el ( * ) asterisco llamado operador de indirección, y el ( & ) ampersand, llamado operador de dirección.

* toma su operando como una dirección de memoria y retorna la información almacenada en ese lugar.

& devuelve la dirección de memoria de su operando. Aplicación práctica del uso de apuntadores

Progra1.cpp

#include <iostream.h> #include <conio.h> void main(void)

{

int x, y; //Define x, y variables enteras.

int *p, *q; //Define p y q variables tipo apuntador. p = &x; //asigna a p la dirección de x. (p apunta a x). q = &y; //asigna a q la dirección de y. (q apunta a y). *p = 10; //almacena en x el valor 10.

*q = *p * 2; //almacena en y el valor de x multiplicado por 2 (20). y = y + *p; //a la variable y le suma el valor en x (20+10).

cout<<”El contenido de X es:” <<*p <\n; //imprime el contenido de x (10). cout<<,”El contenido de Y es:” <<*q; //imprime el contenido de y (30). getch();

}

El resultado de la impresión en pantalla de Progra1 se puede visualizar en la gráfica 1.

(18)

18 Lección 2: Variables Automáticas y Apuntadores

Las variables automáticas se crean en tiempo de compilación y se destruyen al terminar la ejecución del módulo donde fueron declaradas. Aunque no es estrictamente necesario, se pueden manejar las variables automáticas por medio de apuntadores, se puede apreciar en el siguiente ejemplo.

Implementación de variables automáticas Progra2.cpp #include <iostream.h> #include <conio.h> void main() { int automatica ; int *apunt ; automatica = 100 ; apunt = &automatica ; clrscr();

cout << "VALOR=" << automatica << " \n"; *apunt= 200 ;

cout << "VALOR=" << automatica << " \n"; getch();

}

Análisis de Progr2.cpp

Progra2.cpp es el nombre del programa con su respectiva extensión .cpp.

En la línea 1 y 2, se incluyen las librería o archivos de cabecera necesarios para el buen funcionamiento del programa y soportar la sintaxis del lenguaje, se pueden incluir otras según se requieran.

En la línea 3, se define la función principal main() de tipo void, lo que indica que no retorna ningún valor.

En la línea 4, se da apertura a la función por medio de una llave, dentro de ella van las instrucciones del programa y se cierra cuando finalizan las instrucciones o el código.

En la línea 5, se declara la variable llamada automática que es de tipo entero. En la línea 6, se declara el apuntador llamado apunt, de tipo entero, que apunta a objetos de tipo int.

(19)

19 En la línea 7, Se asigna el valor 100 a la variable automatica.

En la línea 8, se asigna a apunt la dirección de memoria de la variable automática En la línea 9, se limpia pantalla por medio de la función clrscr() para que las salidas en pantalla de anteriores ejecuciones sigan mostrándose.

En la línea 10, se muestra la salida en pantalla del valor de la variable automática que es 100.

En la línea 11, se asigna el valor 200 al objeto apuntado por apunt. Es decir a la variable automática.

En la línea 12, se muestra en pantalla el nuevo valor que tomó la variable automática que para este caso es 200.

En la línea 13 se introduce la función getch() que hace la ejecución del programa termine solo cuando se presione una tecla.

Finalmente en la línea 14 se cierra la llave indicando que terminan las instrucciones de la función principal.

El resultado de Progra2.cpp se puede observar en la figura 2, que se muestra a continuación.

Figura 2 Salida en pantalla de progra2.cpp

Más acerca de apuntadores

Un apuntador es una variable que solo puede contener un valor a la vez, por lo que solo puede apuntar a un objeto al mismo tiempo.

Por otro lado, una variable cualquiera puede ser apuntada (referenciada) por varios apuntadores, ya que su dirección de memoria puede ser almacenada en distintas variables a la vez.

(20)

20 Al declarar un apuntador, se está especificando el tipo de variable al que va a apuntar.

Por ejemplo, no podrá declararse un apuntador a objetos de tipo int y después intentar utilizarlo para apuntar a objetos de tipo float.

Cuando se desee manejar un apuntador a cualquier tipo de objeto, se puede declarar de tipo void, como en:

void *apuntadores ;

Aplicación de apuntadores de tipo void

En el siguiente listado se muestra un ejemplo de aplicación de un apuntador de tipo void. Progra3.cpp #include <iostream.h> #include <conio.h> void main() { int entero=1 ; float decimal=1.1 ; void *apvarios=&entero;

// apvarios APUNTA A entero *(int *)apvarios="1" // ASIGNA 1 AL OBJETO APUNTADO POR apvarios cout << "El valor de entero es: "<<entero<<"\n" ; apvarios=&decimal ;

// apvarios APUNTA A decimal *(float *)apvarios="1.1" // ASIGNA 1.1 AL OBJETO APUNTADO POR apvarios cout << "El valor de decimal es: "<<decimal ;

getch(); }

Análisis de Progra3.cpp

Analicemos la siguiente instrucción: *(int *)apvarios = 1 ;

En donde:

Apvarios, es un apuntador de tipo void.

(int *)apvarios está forzando a que apvarios apunte a objetos de tipo int. *(int *)apvarios se refiere a un objeto de tipo entero apuntado por apvarios. En la grafica 1.1 se obtiene la salida en pantalla de Progr3.cpp.

(21)

21 Figura 3 Salida en pantalla de progra3.cpp

Lección 3: Apuntadores y Cadenas

Una cadena se puede definir como un arreglo de caracteres cuyo último elemento es el caracter nulo. Utilizando la nomenclatura de arreglos, podemos declarar un arreglo llamado nombre[ ] de tipo carácter asignándole la frase COMERCIO:

char nombre[ ] = "COMERCIO" ;

La misma asignación puede realizarse por medio de apuntadores, como se observa en el siguiente enunciado.

char *nombre = "COMERCIO" ;

En este caso se define a nombre como un apuntador que apunta a datos de tipo char, al que se le asigna la frase COMERCIO.

Aplicación de apuntadores a cadenas Progra4.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> void main() {

char *nombre = "COMERCIO" ; clrscr(); gotoxy(30,12); cout<< "!! HOLA, " ; puts(nombre); gotoxy(43,12); cout << " !!"; getch(); }

(22)

22 En la figura 4, se visualiza la salida en pantalla de progra4.cpp, se presentan dos funciones objeto de análisis.

La función gotoxy() posiciona el mensaje o la salida en pantalla “!! HOLA” en las coordenadas x=30 y y=12.

La función puts() es propia para el manejo de cadena de caracteres, muestra en pantalla el contenido de la variable nombre.

Figura 4 Salida en pantalla de progra4.cpp

Arreglos de apuntadores

Los apuntadores pueden manejarse en un arreglo, de tal forma que:

char nombres[ ][5] = { "HUGO", "PACO", "LUIS" } ; es la declaración de un arreglo de cadenas, con asignación de valores iniciales. Su equivalente en notación de apuntadores es:

char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ;

En el que se declara un arreglo de apuntadores con tres valores asignados. El programa completo para el manejo de este ejemplo se presenta en el Progra5.cpp. Implementación de arreglos de apuntadores

Progra5.cpp #include <iostream.h> #include <conio.h> #include <string.h> void main() {

(23)

23 char invitado[11];

int bandera; clrscr(); gotoxy(20,5); cout << "CUAL ES SU NOMBRE ?: " ; gotoxy(50,10); cin>> invitado ; gotoxy(20,7); for( int x = 0 ; x <3 ; x++ ) if(strcmp(invitado, nombres[x])== 0) bandera=0; if(bandera== 0)

cout << "!! SIGA AMIGO ES BIENVENIDO " << invitado << " !!"; else cout << "!! USTED NO ES BIENVENIDO, " << invitado << " !!"; getch();

}

En la figura 5, se visualiza el resultado de la salida en pantalla de Progra5.cpp, en el que se incluye la librería <string.h> necesaria para el manejo de cadena de caracteres con las funciones de comparar cadena (strcmp); la cual compara el nombre solicitado por teclado con los nombres asignados al arreglo previamente, en caso de que coincida muestra el primer mensaje de bienvenida, de lo contrario muestra el segundo mensaje.

Figura 5 Salida en pantalla de progra5.cpp

Lección 4: Paso de Arreglos Como Parámetros

Un arreglo puede pasarse como parámetro a una función. Si tuviera que pasarse por valor un arreglo muy grande, sería un desperdicio de memoria y que es función de los programadores optimizar este recurso. En el Lenguaje C++, el paso

(24)

24 de arreglos se hace por referencia, ya que el nombre del arreglo corresponde a un apuntador al primer elemento del arreglo.

Al pasar un parámetro correspondiente a un arreglo, se pasa la dirección del primer elemento, por lo que la función invocada puede modificar cualquier elemento del arreglo. El siguiente programa maneja una función llamada valor(), la cual recibe como parámetro un arreglo de caracteres.

Implementación de paso de arreglos como parámetros Progra6.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void valor(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,10); cout << "INGRESE UNA FRASE CORTA: "; gets(cadena); gotoxy(10,12);

valor(cadena); getch(); }

void valor(char arreglo[ ]) { int x=0; while(arreglo[x]) { cout << arreglo[x] ; x++; } }

El resultado de la salida en pantalla de progra6.cpp se visualiza en la figura 6, que se muestra a continuación, en la que un arreglo de caracteres llamado cadena se pasa como parámetros a la función llamada valor.

(25)

25 Figura 6 Salida en pantalla de progra6.cpp

Implementación de la función valor() por medio de apuntadores

En el siguiente listado se muestra el manejo de la función valor(), por medio de apuntadores. Progra7.cpp #include <iostream.h> #include <conio.h> #include <stdio.h> #include <string.h> void valor(char *); void main() { char cadena[81]; clrscr(); gotoxy(10,5); cout << "ESCRIBA UNA CADENA: "; gets(cadena);

gotoxy(10,7); valor(cadena); getch();

}

void valor(char *apuntador) {

while(*apuntador) cout << *apuntador++ ; }

Analizando este listado y comparándolo con el progra6.cpp se observa que la diferencia radica en el parámetro de la función llamada valor() que par este caso es un apuntador de tipo char que guarda una cadena y para el progra6.cpp es un arreglo de caracteres, que igualmente almacena una cadena y posteriormente

(26)

26 despliega el resultado, tal como se puede evidenciar en la figura 7, que se muestra a continuación.

Figura 7 Salida en pantalla de progra7.cpp

Paso de funciones como parámetros

Toda función tiene asociada una dirección de inicio de código, la cual puede pasarse un apuntador como parámetro cuando se llama o se invoca a otra función. He aquí un ejemplo como aplicación al paso de funciones como parámetros a funciones.

Implementación del paso de funciones como parámetros

Progra8.cpp

#include <iostream.h>

#include <string.h> #include <conio.h>

int cmpcad(char*, char*);

void compara(char*, char*, int(*)(char*, char*));

void main() {

char cadenax[80], cadenay[80]; clrscr();

gotoxy(10,5);

cout << "ESCRIBA UNA CADENA : " ; cin>> cadenax; gotoxy(10,7);

cout << "ESCRIBA OTRA CADENA : " ; cin>> cadenay; gotoxy(10,9);

compara(cadenax, cadenay, cmpcad); gotoxy(1,24);

}

void compara(char *cadena1, char *cadena2, int (*cmpcad)(char*, char*)) {

(27)

27 cout << "LAS CADENAS SON IGUALES";

else cout << "LAS CADENAS SON DISTINTAS"; }

int cmpcad(char *x, char *y) {

return(strcmp(x,y)); }

El resultado de la compilación y posterior ejecución del listado llamado progra8,cpp se conjuga en la salida en pantalla como se muestra en la figura 8.

Figura 8 Salida en pantalla de progra8.cpp

Explicando la expresión que puede ser un tanto desconocida del listado anterior, la expresión:

int(*cmpcad)(char*, char*).

Establece que cmpcad es un apuntador a una función, la cual devuelve un valor de tipo entero.

Lección 5: Apuntadores a Apuntadores

Tal como se trató al inicio de capítulo, un apuntador también es una variable. Su dirección puede ser almacenada por otra variable apuntador del mismo tipo, por lo que puede hablarse de un apuntador a un apuntador.

Esto puede difundirse para dos o más variables, como se observa en el ejemplo siguiente de apuntadores a apuntadores.

(28)

28 Implementación de apuntadores a apuntadores

Progra9.cpp #include <iostream.h> #include <conio.h> void main() { int x, *a, **b, ***c ; // 1 clrscr(); a = &x ; // 2 *a = 100 ; // 3 b = &a ; // 4 **b += *a ; // 5 c = &b ; // 6 ***c += **b + *a ; // 7 cout << " *a=" << *a << " \n" ; cout << " **b=" << **b << " \n" ; cout << "***c=" << ***c << " \n" ; getch(); }

En el listado se analizan tres variables de tipo apuntador que apuntan a objetos de tipo entero, es decir que *a es un apuntador, **b es un apuntador a apuntador y ***c es un apuntador a apuntador a apuntador.

Figura 9 Salida en pantalla de progra9.cpp

Operaciones con apuntadores

 Se pueden realizar asignaciones entre punteros. int a = 15;

(29)

29 q = &a;

p = q; /* se asigna la dirección que contiene q a p */ cout<<p; /* imprime la dirección almacenad en p. */  Se pueden operar solamente el +, el -, el ++ y el --.

Int p;

p = p + 1; p avanza un entero. p = p – 2; p retrocede dos enteros. p++; p apunta al siguiente entero. p--; p apunta al entero anterior.  Los punteros se pueden comparar.

int a; int *b, *c; if (b + n > c) b = b +2;

Como aplicación a las operaciones que se pueden realizar con apuntadores, se presenta el siguiente código en un programa que emplea los operadores (& y *). Progra10.cpp #include <iostream.h> #include <conio.h> int main() { clrscr();

int a; //a es un puntero

int * ap; //ap es un apuntador a un puntero a = 7;

ap= & a; //ap toma la dirección de a cout <<"la dirección de a es " <<&a; cout<<" \n el Valor de ap es " << ap; cout <<"\n el valor de a es" <<a; cout<<"\n el valor de *ap es " << *ap;

cout<<"\n\n\n Mostrando los valores de * y &" ; cout <<"\n &* ap = " <<&*ap;

cout <<"\n *& ap = " << *&ap; getch();

return 0; }

(30)

30 Análisis de Progra10.cpp

Tal como se observa en la imagen es la salida en pantalla del programa en el que se aprecia la diferencia entre el valor de la variable a que devuelve un valor numérico igual a 7; mientras que la variable ap devuelve la dirección de memoria de a.

(31)

31 CAPITULO 2: GESTIÓN DINÁMICA DE MEMORIA

Introducción

La gestión dinámica de memoria hace referencia a la forma enque se almacenan los datos en la computadora. Se aborda la temática relacionada con los tipos de datos que maneja el lenguaje C++ y el número de bytes requeridos para almacenar cada tipo de datos, aunque este tema es transparente para el usuario normal de un sistema computacional, si debe ser motivo de preocupación para los programadores, que están llamados a hacer buen uso de los recursos tanto físicos como lógicos.

Por otro lado se aborda la temática relacionada con los tipos de varibles, variables estáticas y dinámicas. Así como también el uso y la aplicabilidad del operador sizeof en la gestión dinámica de memoria.

En la memoria del computador se guardan no solo las constantes y variables sinó también las instrucciones del programa. Cada objeto y cada instrucción en la memoria tienen previamente asignada una dirección única. Sólo se carga el programa principal en la memoria inicialmente. El resto de subprogramas, algoritmos y funciones se va cargando según se van requiriendo.

Lección 6: Conceptos Básicos de Memoria

Para iniciar este capítulo es importante darle un vistazo a la memoria de la computadora. Si usted ya sabe cómo funciona la memoria de la computadora, se puede saltar esta sección. Sin embargo, si no está seguro, es importante que la lea, le ayudara a comprender mejor ciertos aspectos de la programación.

La computadora usa memoria de acceso aleatorio (RAM) para guardar información mientras está en funcionamiento. La RAM se encuentra en circuitos integrados o chips en el interior de la computadora. La RAM es volátil, lo que significa que es borrada y reemplazada con nueva información tan pronto como se necesita. La volatilidad también significa que la RAM “recuerda” solamente mientras la computadora está encendida, y pierde su información cuando se apaga o se reinicia la computadora.

Cada computadora tiene una determinada cantidad de RAM instalada. La cantidad de RAM en un sistema se especifica por lo general en Megabytes (Mb) por ejemplo 256 Mb, 512 Mb, en ese orden de ideas se dice que un byte es la unidad

(32)

32 de medida fundamental de la memoria de una computadora, de los cuales se obtiene los Kilobytes, Megabytes, Gigabytes, siendo estos los más usados. Un kilobytes de memoria equivale a 1,024 bytes.

Tipo de datos y bytes requeridos en memoria

Para darse una idea de que tantos bytes se necesitan para guardar determinados tipos de datos se pone a disposición la tabla 2.1donde se muestran los espacios requeridos para guardar datos en la memoria del computador.

Tabla1: Bytes requeridos para los tipos de datos

DATOS BYTES REQUERIDOS

La letra X El número 100 El número 125.12

La frase Aprenda usted mismo Una página escrita completamente

1 2 4 21

3000 (Aproximadamente)

La RAM en la computadora está organizada en forma secuencial, un byte tras otro. Cada byte de memoria tiene una dirección única mediante la cual es identificada, una dirección que también lo distingue de todos los otros bytes de la memoria. Las direcciones son asignadas a la memoria en orden, comenzando en 0 y aumentando hasta llegar al límite de memoria instalada en el sistema.

Para ampliar un poco más la conceptualización a cerca de los tipos de datos se define todo el posible rango de valores que una variable puede tomar al momento de ser ejecutada en el programa al igual que en toda la vida útil del propio programa.

Lección 7: Tipos de datos comunes

En la tabla 1 se muestran los tipos de datos y el espacio en memoria de cada dato en bytes, sin embargo también es posible determinar el tamaño en bites para los tipos de datos más comunes utilizados frecuentemente en C++, tal como se muestra en la Tabla 2:

(33)

33 Tabla 2: Otros tipos de datos comunes

TIPO DATO ESPACIO

MEMORIA RANGO PERMITIDO

unsigned char 8 bits 0 a 255

Char 8 bits -128 a 127

short int 16 bits -32,768 a 32,767 unsigned int 32 bits 0 a 4,294,967,295

Int 32 bits -2,147,483,648 a 2,147,483,647

unsigned long 32 bits 0 a 4,294,967,295

Enum 16 bits -2,147,483,648 a 2,147,483,647 Long 32 bits -2,147,483,648 a 2,147,483,647 Float 32 bits 3.4 x 10-38 a 3.4 x 10+38(6 dec) Doublé 64 bits 1.7 x 10-308 a 1.7*10+308(15 dec) long doublé 80 bits 3.4 x 10-4932 a 1.1 x 10+4932

Void sin valor

Fuente: http://www.wikilearning.com/tutorial/tutorial_de_c++-tipos_de_datos/9773-4

Es posible realizar una representación de la Tabla 2 en términos de programación para visualizar el tipo de datos y el espacio requerido en memoria presentado en bits como la mínima medida del sistema binario. Tal como se puede ver en el listado siguiente llamado Progra11.cpp.

Progra11.cpp

#include <iostream.h> #include <conio.h> int main ()

{

cout<<"TIPOS DE DATOS Y TAMANO EN BITS\n\n";

cout<< "El tamano del tipo de dato int es :"<<sizeof(int)*8<<" bits\n"; cout<< "El tamano del tipo de dato short es :"<<sizeof(short)*8<<" bits\n"; cout<< "El tamano del tipo de dato long es :"<<sizeof(long)*8<<" bits\n"; cout<< "El tamano del tipo de dato char es :"<<sizeof(char)*8<<" bits\n"; cout<< "El tamano del tipo de dato float es :"<<sizeof(float)*8<<" bits\n"; cout<< "El tamano del tipo de dato double es:"<<sizeof(double)*8<<" bits\n"; getch();

return 0; }

Ya se tiene el código fuente guardado con el nombre de Progra11.cpp, ahora solo resta compilar y ejecutar el código para obtener el siguiente resultado como salida en pantalla.

(34)

34 Figura 11 Salida en pantalla de progra11.cpp

Usos de la Memoria RAM

Seguramente con frecuencia alguien se ha planteado la siguiente pregunta ¿Para qué se usa la memoria RAM de la computadora?. Tiene varios usos, pero solamente uno, el almacenamiento de datos, le interesa al programador. Los datos significan la información con la cual trabaja un programa. Ya sea que el programa esté trabajando con una lista de direcciones, monitoreando la bolsa de valores, manejando un presupuesto, controlando la nómina de una empresa o cualquier otra tarea, la información de (nombres, precios de acciones, gastos, salarios) es guardada en la RAM de la computadora mientras el programa esté ejecutándose. Hasta el momento, la mayoría de los programas se han realizado definiendo variables, sin tener la preocupación de qué se está realizando internamente en el computador, muchas veces en forma indiscriminada, es decir sin una verdadera depuración, pero existen ocasiones en que no se sabe cuánta memora se requerirá para la ejecución de un determinado programa; por ejemplo, si deseamos realizar un procesador de textos, de entrada no se conoce cuál va hacer la longitud del texto.

Por eso a veces es necesario poder reservar memoria según se va necesitando. Además de esta forma los programas en ejecución aprovecharán mejor la memoria del computador, usando sólo los recursos necesarios.

Realmente la utilidad de asignación dinámica de memoria será aplicada en gran medida en los capítulos relacionados con las estructuras de datos lineales, tema que se trata en la unidad dos.

De acurdo a lo anterior se puede identificar dos tipos de variables: estáticas y dinámicas para ampliar estos conceptos se invita a revisar la temática tratada en la Lección 3.

(35)

35 Lección 8: Tipos de Variables

Dependiendo del uso que se le dé a las variables por parte del programador en una rutina o tarea específica, se pueden identificar dos tipos de variables, las variables dinámicas y variables estáticas.

Variables estáticas

Las variables estáticas tal como se recuerda en los inicios de los fundamentos de programación, son aquellas que el programador les asigna memoria antes de la ejecución del programa o de una función. Las variables estáticas se llaman mediante el nombre de la misma, que ha sido declarada por el programador y su función principal dentro de un programa es almacenar información dependiendo del tipo de dato con que se haya declarado.

Aplicación de las variables estáticas

A continuación se presenta el código fuente de un programa que hace uso de las variables estáticas. Progra12.cpp #include <stdio.h> #include <iostream.h> #include <stdlib.h> #include <conio.h>

int priNumero; /* Las variables */ int segNumero; int suma; void main() { clrscr(); priNumero = 136; segNumero = 369;

suma = priNumero + segNumero; cout<<"La suma es " <<suma; getch();

}

Análisis de Progra12.cpp

En el código del programa se hace uso de tres variables de tipo entero que son variables estáticas ellas son: priNumero que se le asigna el valor 136, mientras

(36)

36 que a la variable segNumero se le asigna el valor 369, de igual manera a la variable suma calcula el resultado de sumar el valor de las dos variables.

Aunque se trata de un programa sencillo de entender que solo muestra la suma de dos variables estáticas, vale la pena poner a disposición la salida en pantalla tal como se presenta a lo largo del curso.

Figura 12 Salida en pantalla de progra12.cpp

Lección 9: Variables Dinámicas

Las variables dinámicas deben su nombre al hecho de que pueden ser creadas y destruidas durante el tiempo de ejecución de un módulo.

Para el manejo de variables dinámicas se hace indispensable la utilización de apuntadores, así como de funciones especiales para la asignación y liberación de la memoria correspondiente a dichas variables.

Aplicación de las variables dinámicas

A continuación se presenta el código fuente completo de un programa que hace uso de las variables dinámicas.

Progra13.cpp #include <iostream.h> #include <conio.h> void main() { int x, *a, **b, ***c ; // 1 clrscr(); a = &x ; // 2 *a = 100 ; // 3

(37)

37 b = &a ; // 4 **b += *a ; // 5 c = &b ; // 6 ***c += **b + *a ; // 7 cout << " *a=" << *a << " \n" ; cout << " **b=" << **b << " \n" ; cout << "***c=" << ***c << " \n" ; getch(); } Análisis de Progra13.cpp

En el listado del progra13.cpp se declaran tres variables de tipo apuntador que apuntan a datos de tipo entero, ellas son, *a, **b y **c. El apuntador a almacena la dirección de la variable x, luego a al apuntador a se le asigna el valor de 100, lo que indica que x vale 100, mientras que **b precedida de dos asteriscos indica que es una variable que apunta a un apuntador, para este caso b guarda la dirección de el puntero a, y ***c es un apuntador a apuntador a apuntador, lo cual puede simplificarse en que c guarda la dirección de b. El resultado del listado Progra13.cpp se puede visualizar en la Figura 13.

Figura 13Salida en pantalla de progra13.cpp

Lección 10: Asignar y Liberar Espacios de Memoria

En el lenguaje C existen entre otras las funciones Malloc() y Free() para la asignación y liberación de memoria dinámicamente respectivamente.

Cuando se ejecuta un programa, el sistema operativo reserva una zona de memoria para el código o instrucciones del programa y otra para las variables que se usan durante la ejecución. A menudo estas zonas son la misma zona, es lo que se llama memoria local. También hay otras zonas de memoria, como la pila, que se usa, entre otras cosas, para intercambiar datos entre funciones. El resto, la

(38)

38 memoria que no se usa por ningún programa es lo que se conoce como "heap" o montón.

Cuando el programador use memoria dinámica, normalmente usará memoria del montón, y no se llama así porque sea de peor calidad, sino porque suele haber realmente un montón de memoria de este tipo disponible para ser usado en la medida que se requiera.

Profundizando un poco en la asignación dinámica, se encuentra el operador sizeof, el cual determina el tamaño en bytes que se requiere en la asignación dinámica de memoria, ya sea por medio de los operadores New y Delete, o por las funciones Malloc y Free, de un arreglo o de cualquier otro tipo de datos,

Ejemplo utilizar el operador sizeof en una función con el propósito de determinar el tamaño en bytes de un parámetro.

Aplicación del operador sizeof.

En el siguiente listado se evidencia la aplicación y uso del operador sizeof en cada variable el cual devuelve el número de bytes dependiendo del tipo de variable. Implementación del uso de sizeof

Progra14.cpp #include <iostream.h> #include <conio.h> void main() { char P; short R; int I; long L; float F; double D; long double LD; clrscr(); gotoxy(1,2);

cout<<"valores utilizando sizeof para cada una de la varibles \n\n"; cout<<" variable P = " <<sizeof P;

cout<<"\t tipo char = " <<sizeof (char); cout<<"\n variable R = " <<sizeof R; cout<<"\t tipo short = " <<sizeof (short); cout<<"\n variable I = " <<sizeof I; cout<<"\t tipo int = " <<sizeof (int); cout<<"\n variable L = " <<sizeof L; cout<<"\t tipo int = " <<sizeof (long);

(39)

39 cout<<"\n variable F = " <<sizeof I;

cout<<"\t tipo float = " <<sizeof (float); cout<<"\n variable D = " <<sizeof D; cout<<"\t tipo double = " <<sizeof (double); cout<<"\n variable LD = " <<sizeof LD; cout<<"\t tipo int = " <<sizeof (long double); cout<<"\n variable I = " <<sizeof I;

cout<<"\t tipo int = " <<sizeof (int); getch();

}

Análisis de Progra14.cpp

En la siguiente gráfica se visualiza el resultado del progra13.cpp en laque se puede apreciar el resultado almacenado en la variable y los bytes requeridos para cada tipo de dato.

(40)

40 CAPITULO 3: OPERADORES Y FUNCIONES EN LA GESTIÓN DE MEMORIA

Introducción

En el presente capítulo se trata la temática relacionada con los operadores y las funciones utilizadas para la gestión de memoria, en ese sentido se realizan programas como aplicación a los operadores new y delete propios de C++, al igual que las funciones malloc(), free() y calloc() y realloc() propias de C.

Al igual que en cada uno de los capítulos, se presentan programas de aplicación que dan respuesta a al temática planteada, estos programas están previamente compilados y depurados para garantizar que el estudiante pueda editarlos analizalos y plantear mejoras que contribuyan a su formación y al adquisición de la competencia del curso desarrollada en cada uno de sus capítulos.

Lección 11: Operadores New y Delete

El lenguaje C++ cuenta con dos operadores preconstruidos, ellos son: New y Delete, por esta razón no se requiere incluir ninguna librería o archivo de cabecera para utilizarlos.

El operador New: Realiza una labor parecida a la de la función malloc(), asignando un bloque de memoria según sea requerido.

El operador Delete: Libera un bloque de memoria asignada por New en tiempo de ejecución, de manera semejante a como lo hace la función free().

La sintaxis para el uso del operador new es: Apuntador = new tipo_de_dato;

Este operador hace una petición al sistema operativo para que se le asigne un espacio en memoria, con el tamaño acorde al tipo de datos (vale la pena recordar la función sizeof), si este espacio está disponible, la operación regresa la dirección real que se otorga, en caso de no haber espacio regresa el valor de NULL (0), La sintaxis para el uso del operador delete es:

(41)

41 La ejecución de este operador provoca que se libere espacio, dejando como valor indefinido, es decir el sistema operativo lo considera como memoria disponible. Hay una regla de oro utilizada por algunos autores, se refiere a que cuando se usa memoria dinámica, toda la memoria que se reserve durante el programa hay que liberarla antes de salir del programa. No seguir esta regla es una actitud muy irresponsable, y en la mayor parte de los casos tiene consecuencias desastrosas que atentan con la estabilidad de su sistema. No es bueno fiarse de lo que diga el compilador, de que estas variables se liberan solas al terminar la ejecución del programa, no siempre puede ser así.

Aplicación de los operadores New y Delete

Se presenta un ejemplo como aplicación de los operadores de C++, utilizados para asignar y liberar memoria dinámicamente.

“En el listado se declaran las variables index de tipo entero y los apuntadores point1 y point2 ambos de tipo entero.

Progra15.cpp # include <iostream.h> # include <conio.h> main() { clrscr();

int index, *point1, *point2; point1 = &index;

*point1 = 77; point2 = new int; *point2 = 173;

cout <<"SALIDA DE LAS VARIABLES index, *point1 Y *point2\n\n";

cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; point1 = new int;

point2 = point1; *point1 = 999;

cout <<"Los valores son " << index <<" " << *point1 << " "<< *point2 <<'\n'; delete point1;

float *float_point1, *float_point2 = new float; float_point1 = new float;

*float_point2 = 3.14159;

*float_point1 = 2.4 * (*float_point2); delete float_point2;

delete float_point1; char *c_point;

(42)

42 c_point = new char;

delete c_point;

c_point = new char [sizeof(int) + 133]; delete c_point;

getch(); return 0; }

Análisis de Progra15.cpp

El resultado de la ejecución del listado llamado Progra16.cpp se visualiza en la Figura 15.

Figura 15 Salida en pantalla de progra15.cpp

 En las primeras líneas del programa, se hace uso de los punteros tal como se hacen también en C.

 point2 ilustra el uso del operador new. Este operador requiere un modificador que debe ser un tipo. La parte new int significa que se crea un nuevo entero en la memoria, y devuelve la localización del entero creado. Esta localización es asignada a point2. La siguiente línea asigna 173 al entero al que apunta point2. Es importante distinguir entre point2, la localización del entero, y *point2, el entero. El puntero point2 apunta ahora a una variable entera que se ha reservado dinámicamente, y que puede utilizarse de igual forma que se hacía en C. Como ejemplo, se imprime el valor al que apunta.

 A continuación, se reserva memoria para una nueva variable, y point2 se refiere a la misma variable reservada dinámicamente a la que apunta point1. En este caso, la referencia a la variable a la que point2 apuntaba previamente se ha perdido, y nunca podrá ser utilizada o su memoria liberada. Sólo cuando se vuelva al sistema operativo se liberará la memoria que ocupaba. Por tanto, no debe utilizarse.

(43)

43  Ya que el puntero point1 en sí no ha cambiado, apunta realmente al dato original. Este dato podría referenciarse otra vez utilizando point1, pero no es una buena práctica de programación, ya que no hay garantía de lo que el sistema pueda hacer con el puntero o el dato. La localización del dato queda libre para ser reservada en una llamada subsiguiente, y será pronto reutilizada en cualquier programa.

 Ya que el operador delete está definido para no hacer nada si se le pasa un valor NULL, se puede liberar la memoria ocupada por un dato al que apunta un puntero NULL, ya que realmente no se está haciendo nada. El operador delete sólo puede utilizarse para liberar memoria reservada con el operador new. Si se usa delete con cualquier otro tipo de dato, la operación no está definida, y por tanto nada sucede.

 En el programa también declaramos algunas variables reales, y se realizan operaciones similares a las anteriores. De nuevo esto ilustra que en C++ las variables no tienen que ser declaradas al comienzo de cada bloque. Una declaración es una sentencia ejecutable y puede entonces aparecer en cualquier lugar en la lista de sentencias ejecutables.

 Finalmente, ya que el operador new requiere un tipo para determinar el tamaño de un bloque dinámicamente reservado, se muestra cómo reservar un bloque de tamaño arbitrario. Esto es posible utilizando la construcción de las últimas líneas del programa, donde un bloque de 37 caracteres de tamaño (37 bytes) es reservado. Un bloque de 133 bytes mayor que el tamaño de un entero se reserva posteriormente. Por tanto, el operador new se puede utilizar con la misma flexibilidad de la función malloc() de C.  Cuando los datos reservados dinámicamente son borrados con delete,

todavía quedan en memoria. Si repetimos la instrucción cout inmediatamente después de utilizar delete, veremos que todavía se conservan los valores. Si la repetimos de nuevo antes de dejar el programa, cuando el espacio que ocupaban debe haber sido sobrescrito, veremos que ya no es así. Incluso aunque el compilador nos dé los números correctos, no es una buena práctica pensar que esos datos están ahí todavía, porque en un programa dinámico largo la memoria se usará continuadamente.  Las funciones estándar utilizadas en C para manejo dinámico de memoria,

malloc(), calloc() y free(), también se pueden utilizar en C++ de la misma forma que en C. Los operadores new y delete no deben mezclarse con estas funciones, ya que los resultados pueden ser impredecibles. Si se está partiendo de código C, lo mejor es continuar utilizando las funciones en las nuevas líneas de programa. Si no es así, se deben utilizar los nuevos

(44)

44 operadores, ya que se han construido como parte del lenguaje en sí, más que añadirse, y por tanto son más eficientes”1.

 Cuando se utiliza new para reservar memoria para un vector, el tamaño del vector se sitúa entre corchetes, siguiendo al tipo:

int *intvector;

intvector = new int [20]; y se libera:

delete [ ] intvector;

Lección 12: Más acerca de la Implementación de New y Delete

El siguiente muestra un ejemplo de aplicación de los operadores new y delete. Progra16.cpp #include <iostream.h> //1 #include <conio.h> //2 void main() //3 { //4 clrscr(); //5 int *numero; //6 numero = new int ; //7 *numero = 10 ; //8 cout <<"VALOR DE NUMERO\n\n"; //9

cout <<"El resultado de numero es:" <<*numero ; //10 delete numero ; //11

getch(); //12 } //13

En el listado llamado Progra16.cpp, se supone que la reservación será exitosa porque existe espacio suficiente en el montículo.

Del listado se puede analizar lo siguiente:

En las líneas 1 y 2 se incluyen las librerías requeridas para la ejecución del programa sin errores de compilación y ejecución.

En la línea 3 se incluye la función principal main() la cual no retorna ningún valor por estar declarada como tipo void.

La línea 4 se incluye la llave que da apertura a la función principal.

1

(45)

45 La línea 5 se incluye la función clrscr() utilizada para limpiar pantalla de ejecuciones anteriores del programa.

En la línea 6 se declara un puntero llamado numero y es de tipo entero.

En la línea 7 se reserva un bloque de memoria dinámica de 2 bytes para manejarlo por medio de numero.

En la línea 8 Asigna el valor 10 al objeto apuntado por numero. En la línea 9 se despliega un mensaje en pantalla.

En la línea 10 se despliega el contenido del objeto apuntado por numero. La línea 11, libera el espacio de memoria asignado o manejado por numero.

En la línea 12 se incluye la función getch(), la cual hace una parada del programa a la espera que se presione una tecla para finalizar la ejecución del programa. Finalmente la línea 13, se incluye la llave que cierra las instrucciones que hacen parte de la función principal.

El resultado de la salida en pantalla se puede visualizar en la Figura16.

Figura 16 Salida en pantalla de progra16.cpp

Pero ¿quién asegura que el espacio requerido por new está disponible?. Para controlar esta situación y evitar un mensaje de error por parte del sistema en tiempo de ejecución, en el listado siguiente se propone una nueva versión del programa.

Implementación de New para verificación de memoria Progra17.cpp #include <iostream.h> #include <stdlib.h> #include <conio.h> void main() { int *numero;

(46)

46 if((numero = new int)==NULL)

{

cout << "NO hay espacio suficiente\n"; exit(1);

}

*numero=20 ;

cout <<"Resultado\n\n";

cout << "El resultado de numero es:"<<*numero ; delete numero ;

getch(); }

El resultado en pantalla de Progra17.cpp se puede visualizar en la Figura 17.

Figura 17 Salida en pantalla de progra17.cpp

Otro caso a considerar es la reserva de memoria con el operador new para un arreglo, un ejemplo sería la reserva de memoria para crear un arreglo de 25 elementos de tipo double, en el montículo, puede declararse como:

Double *numero;

numero = new double[25];

De igual manera en su forma equivalente se tiene: double *numero = new double[25];

En este ejemplo, se está declarando a número como un apuntador a variables dinámicas de tipo doble; al tiempo que se le asigna el valor retornado por new. El valor retornado por new es la dirección del inicio de un bloque de memoria del tamaño requerido para almacenar 25 elementos de tipo double. En caso de que el montículo no disponga del espacio requerido, new retorna el valor NULL (nulo).

(47)

47 Lección 13: Funciones Malloc() y Free()

La función Malloc()

Es una de las funciones de asignación de memoria propias del lenguaje de programación C (acrónimo de memory allocation). Cuando se usa malloc () se pasa la cantidad de bytes de memoria que se necesita. Malloc () encuentra y reserva un bloque de menoría del tamaño pedido y regresa la dirección del primer byte del bloque. No hay de qué preocuparse sobre que parte de memoria se usa, ya que esto es manejado automáticamente.

La función malloc () regresa una dirección, y su tipo de retorno es un apuntador a tipo void. ¿Por qué void?. Un apuntador a tipo void es compatible con todos los tipos de datos. Como la memoria asignada por malloc () puede ser usada para guardar cualquiera de los tipos de datos de C, es adecuado el tipo de retorno void.

La función Free()

Al igual que malloc(), free() es una función del lenguaje de programación C, utilizado para liberar la memoria asignada por malloc (). Al usar la función free () se debe tener en cuenta la regla de oro explicada en el apartado del operador delete “toda la memoria que se reserve durante el programa hay que liberarla antes de salir del programa”

Al usar las funciones malloc () y free () se debe incluir el archivo de cabecera o librería STDLIB.H.

Ejemplos de aplicación Ejemplo 1

Asigna memoria para un arreglo de 50 enteros Int *numero;

numero = (int * ) malloc (50 * sizeof (int)); Ejemplo 2

Asigna memoria para un arreglo de 10 valores float float *numero;

(48)

48 Lección 14: Aplicación a la asignación de memoria con Malloc() y Free() Una Buena aplicación de las funciones utilizadas para la gestión dinámica de memoria se puede visualizar en el siguiente listado llamado Progra18.cpp.

Progra18.cpp #include <iostream.h> #include <conio.h> #include <stdlib.h> #include <stdio.h> int main() { clrscr(); int *p=NULL; int nbytes=100; p=(int *)malloc(nbytes); if(p==NULL) {

cout<<"Insuficiente espacio en memoria\n"; return -1;

}

cout<< "Bytes Reservados\n\n";

cout<<"Se han asignado " <<nbytes <<" bytes de memoria\n"; free(p);

getch(); }

La salida en pantalla de Progra18.cpp se puede visualizar en la Figura 18 que se muestra a continuación.

Figura 18 Salida en pantalla de progra18.cpp

El resultado del programa muestra un mensaje en pantalla confirmando que se realizó la asignación dinámica de memoria de 100 bytes con éxito.

A continuación se presenta otro ejemplo de aplicación de la asignación dinámica de memoria utilizando las funciones malloc () y free (), con un ingrediente adicional

(49)

49 el uso de una estructura llamada persona, la cual permita almacenar el registro de datos de una persona, dos tipos de datos diferentes, un dato de tipo carácter que almacena el nombre de la persona y otro dato de tipo entero para almacenar la edad.

He aquí la implementación del programa completo. Implementación del uso de Malloc() y Free() Progra19.cpp

// Listado de librerías o archives de cabecera #include<iostream.h>

#include<stdlib.h> #include<conio.h>

// Definición de la función principal void main()

{

clrscr(); int n, i;

// Definición de la estructura persona struct persona

{

char nombre[20]; int edad;

};

// Definición del puntero p de tipo persona utilizado para reservar memoria persona *p;

cout<<"PROGRAMA QUE GUARDA EL REGISTRO DE PERSONAS"<<"\n"; cout<<"\nNUMERO DE PERSONAS A INGRESAR : ";

cin>>n;

// Reserva de memoria dinámica a través de malloc ()

p =(persona *)malloc(sizeof(persona));

// El ciclo for usado para la entrada de los datos de la persona for(i=1;i<=n;i++) { cout<<"\nDIGITE EL NOMBRE " << i <<" : "; cin>>p[i].nombre; cout<<"DIGITE LA EDAD : "; cin>>p[i].edad; cout<<"\n";

(50)

50 }

clrscr();

cout<<"LISTADO DE PERSONAS REGISTRADAS "<<"\n";

// El ciclo for usado para la impresión o visulización de los datos registrados for(i=1;i<=n;i++)

{

cout<<" NOMBRE : "<<p[i].nombre<<"\n"; cout<<" EDAD : "<<p[i].edad<<"\n\n"; }

getch();

// La función free () libera la memoria asignada al apuntador p free (p);

}

Resultado en pantalla del Progra19.cpp

La siguiente imagen muestra el resultado de en pantalla que registra el nombre y la edad de 3 tres personas.

Figura 19 Salida en pantalla de progra19.cpp

Lección 15: Otras funciones para asignar memoria dinámica

Aparte de la función Malloc() tratada en la sección anterior, utilizada para la gestión dinámica de memoria, existen dos funciones adicionales para reservar memoria, ellas son: calloc() y realloc().

La Funcione Calloc()

Referencias

Documento similar