Você está na página 1de 25

1

Captulo 1. Apuntadores

1.1 Conceptos bsicos.


Para entender qu es un puntero veremos primero cmo se almacenan los datos en un computador. La
memoria de un computador est compuesta por unidades bsicas llamadas bits. Cada bit slo puede
tomar dos valores, normalmente denominados alto y bajo, 1 y 0. Pero trabajar con bits no es prctico,
y por eso se agrupan. Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y
por lo tanto nuestro programa, slo 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 direccin, llamada
normalmente direccin de memoria.

1.1.1 Definicin de Apuntadores


Un apuntador es una variable, que almacena como contenido una direccin de memoria, de otra
variable a la que apunta, dicha direccin representa el lugar donde se almacena un dato. Los
apuntadores tienen un tipo de dato especfico y solo pueden apuntar a espacios de memoria con datos
del mismo tipo. Por supuesto, a partir de esa direccin de memoria puede haber cualquier tipo de
objeto: un char, un int, un float, un array, una estructura, una funcin u otro puntero. Seremos nosotros
los responsables de decidir ese contenido.
Con los apuntadores es posible manipular estructuras de datos o asignar memoria dinmica.
Los apuntadores son una de las herramientas ms 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 dinmicas en lenguaje C/C++.

1.1.2 Declaracin de apuntadores


Los apuntadores son variables automticas cuyos valores representan direcciones de memoria
correspondientes a otras variables.

La sintaxis para la declaracin de un apuntador es la siguiente:


tipo * identificador ;

Ejemplo: int *apunt; // Declaracin del apuntador apunt


// Se dice que: "apunt va a apuntar a variables de tipo int"
donde:
apunt es el nombre del apuntador y (*) es el operador de indireccin
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 indireccin utiliza el mismo smbolo que el operador de multiplicacin. En
este caso el asterisco le indica al sistema que se define una variable apuntador. Ejemplos:
int *x; a es un apuntador de tipo entero.
char *y; c es un apuntador de tipo carcter.
double *p, *q; p y q son apuntadores de tipo real doble precisin.

1.1.3 Operadores para trabajar apuntadores


Los operadores utilizados para trabajar variables apuntadores son el ( * ) asterisco llamado operador de
indireccin, y el ( & ) ampersand, llamado operador de direccin.
2

* toma su operando como una direccin de memoria y retorna la informacin almacenada en ese
lugar.

& devuelve la direccin de memoria de su operando.

Veamos un ejemplo llamado Programa1.cpp en el siguiente listado.

1.1.4 Aplicacin prctica 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 direccin de x. (p apunta a x).
q = &y; asigna a q la direccin 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<<*p; imprime el contenido de x (10).
cout<<,*q; imprime el contenido de y (30).
getch();
}

1.2 Variables automticas y apuntadores


Las variables automticas se crean en tiempo de compilacin y se destruyen al terminar la ejecucin del
mdulo donde fueron declaradas. Aunque no es estrictamente necesario, se pueden manejar las
variables automticas por medio de apuntadores, veamos un ejemplo.

1.2.1 Implementacin de variables automticas


Progra2.cpp
#include <iostream.h>
#include <conio.h>
void main()
{
int automatica ; // Se declara la variable automatica.
int *apunt ; // Se declara el apuntador apunt, que apuntar a objetos de tipo int.
automatica = 100 ; // Se asigna el valor 100 a la variable automtica.
apunt = &automatica ; // Se asigna a apunt la direccin de automatica apunt apunta a
// automatica.
clrscr();
cout << "VALOR=" << automatica << " \n"; // 100
*apunt="200" ; // Se asigna el valor 200 al objeto apuntado- // por apunt.
cout << "VALOR=" << automatica << " \n"; // 200
getch();
}
3

1.2.2 Anlisis de Progra2.cpp


Las instrucciones del listado anterior se traducen en la siguiente secuencia, donde los apuntadores se
representan con una flecha (para simular que "apuntan hacia" "sealan" un objeto) y los objetos
apuntados se representan por un cuadro (para simular un recipiente).

INSTRUCCION REPRESENTACION GRAFICA

int automatica ; automatica ?

int *apunt ; ----> ?

automatica = 100 ; automatica 100

apunt = &automatica ; automatica, *apunt

100
apunt ---->

*apunt = 200 ; automatica, *apunt

Apunt ----> 200

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 direccin de memoria puede ser almacenada en distintas variables a
la vez. 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 despus 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 *multiusos;

En el siguiente listado se muestra un ejemplo de aplicacin de un apuntador de tipo void.

1.2.3 Aplicacin de apuntador tipo void

Progra3.cpp
#include <iostream.h>
#include <conio.h>
#define NL
cout << "\n";
void main()
{
int varent="0" ;
float varflot="0.0" ;
void *apmulti="&varent; // apmulti APUNTA A varent
*(int *)apmulti="2" ; // ASIGNA 2 AL OBJETO NL; // APUNTADO POR apmulti
cout << varent ;
4

apmulti="&varflot" ; // apmulti APUNTA A varflot *(float *)apmulti="1.1" ;


// ASIGNA 1.1 AL OBJETO APUNTADO // POR apvoid NL;
cout << varflot ;
NL;
getch();
}

1.2.4 Anlisis de Progra3.cpp


Analicemos la siguiente instruccin:
*(int *)apmulti = 2 ;
en donde:
apmulti es un apuntador de tipo void.

(int *)apmulti est forzando a que apmulti apunte a objetos de tipo int.

*(int *)apmulti se refiere a un objeto de tipo entero apuntado por apmulti.

1.3 Apuntadores y cadenas


Una cadena es un arreglo de caracteres cuyo ltimo elemento es el caracter nulo. Utilizando la
nomenclatura de arreglos, podemos declarar:

char nombre[ ] = "COMERCIO" ;

Esto mismo puede hacerse por medio de apuntadores, como se muestra en siguiente ejemplo.

1.3.1 Implementacin de apuntadores y 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();
}

1.3.2 Arreglos de apuntadores

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


char nombres[ ][5] = { "HUGO", "PACO", "LUIS" } ;
es la declaracin de un arreglo de cadenas, con asignacin de valores iniciales. Su equivalente en
notacin de apuntadores es:

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


en el que se declara un arreglo de apuntadores. El programa completo para el manejo de este ejemplo.
5

1.3.3 Implementacin de arreglos de apuntadores

Progra5.cpp
#include <iostream.h>
#include <conio.h>
#include <string.h>

void main()
{
char *nombres[ ] = { "HUGO", "PACO", "LUIS" } ;
char invitado[11];
int bandera;
clrscr();
gotoxy(30,10);
cout << "CUAL ES SU NOMBRE ? " ; gotoxy(50,10); cin>> invitado ;
gotoxy(30,12);
for( int x = 0 ; x <3 ; x++ ) if(strcmp(invitado, nombres[x])="=" 0)
bandera="0;" if(bandera="=" 0) cout << "!! PASE, ESTIMADO "
<< invitado << " !!"; else cout << "!! FUERA DE AQUI, " << invitado << "
!!"; getch();
}

1.4 Paso de arreglos como parmetros


Un arreglo puede pasarse como parmetro a una funcin. Si tuviera que pasarse por valor un arreglo
muy grande, sera un desperdicio de memoria. En el Lenguaje C++ el paso de arreglos se hace por
referencia, ya que el nombre del arreglo corresponde a un apuntador al primer elemento del arreglo.

Al pasar un parmetro correspondiente a un arreglo, se pasa la direccin del primer elemento, por lo
que la funcin invocada puede modificar cualquier elemento del arreglo. El siguiente programa maneja
una funcin llamada nputs(), la cual recibe como parmetro un arreglo de caracteres.

1.4.1 Implementacin de paso de arreglos como parmetros

Progra6.cpp
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
void nputs(char *);
void main()
{
char cadena[81];
clrscr();
gotoxy(10,10);
cout << "ESCRIBA UNA CADENA: "; gets(cadena); gotoxy(10,12); nputs(cadena); getch(); } void
nputs(char cad[ ]) { int x="0;" while(cad[x]) { cout << cad[x] ; x++; } }
6

1.4.2 Implementacin de la funcin nputs() por medio de apuntadores


En el siguiente listado se muestra el manejo de la funcin nputs(), por medio de apuntadores.

Progra7.cpp
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
void nputs(char *);
void main()
{
char cadena[81];
clrscr();
gotoxy(10,10);
cout << "ESCRIBA UNA CADENA: ";
gets(cadena);
gotoxy(10,12);
nputs(cadena);
getch();
}
void nputs(char *cad)
{
while(*cad) cout << *cad++ ;
}

1.4.3 Paso de funciones como parmetros


Toda funcin tiene asociada una direccin de inicio de cdigo, la cual puede pasarse un apuntador
como parmetro en la invocacin a otra funcin. Veamos un ejemplo de paso de punteros como
parmetros a funciones.

1.4.4 Implementacin del paso de funciones como parmetros

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 cadx[80], cady[80];
clrscr();
gotoxy(10,5);
cout << "ESCRIBA UNA CADENA : " ; cin>> cadx;
gotoxy(10,7);
cout << "ESCRIBA OTRA CADENA : " ; cin>> cady;
gotoxy(10,9);
compara(cadx, cady, cmpcad);
gotoxy(1,24);
7

}
void compara(char *cad1, char *cad2,
int (*cmpcad)(char*, char*))
{
if(!(*cmpcad)(cad1,cad2))
cout << "LAS CADENAS SON IGUALES";
else cout << "LAS CADENAS SON DISTINTAS";
}
int cmpcad(char *x, char *y)
{
return(strcmp(x,y));
}
Expliquemos la expresin que puede ser un tanto desconocida del listado anterior, la expresin:
int(*cmpcad)(char*, char*) establece que cmpcad es un apuntador a una funcin, la cual devuelve un
valor de tipo entero.

1.5 Apuntadores a apuntadores

Como se vio al principio de la unidad, un apuntador tambin es una variable. Su direccin puede ser
almacenada por otra variable apuntador, por lo que puede hablarse de un apuntador a un apuntador.

Esto puede extrapolarse para dos o ms variables, como se observa en el ejemplo siguiente de
apuntadores a apuntadores.

1.5.1 Implementacin 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();
}

1.5.2 Anlisis de Progra9.cpp


Explicando las lneas marcadas en el listado anterior se tiene.

int x, *a, **b, ***c; // 1


8

Se declaran:
x como una variable de tipo entero. a como un apuntador a objetos de tipo entero. b como un apuntador
a un apuntador, el cual a su vez apuntar a objetos de tipo entero. Se dice que b es "el apuntador del
apuntador". c como un apuntador a un apuntador que apunta a otro apuntador, el cual a su vez apunta
a objetos de tipo entero. Se dice que c es "el apuntador del apuntador del apuntador".
La pila lucira as:

a = &x ; // 2

Se asigna, al apuntador a, la direccin de x. La pila lucira as:

*a = 100 ; // 3
Al objeto apuntado por a se le asigna el valor 100. La pila lucira as:

b = &a ; // 4
Al apuntador b se le asigna la direccin del apuntador a. La pila lucira as:

**b += *a ; // 5
Al objeto apuntado por el apuntador apuntado por b se le suma el valor del objeto apuntado por a. La
pila lucira as:
9

c = &b ; // 6
Al apuntador c se le asigna la direccin del apuntador b. La pila lucira as:

***c += **b + *a ; // 7
Se asigna al objeto apuntado por el apuntador apuntado por el apuntador c, el valor del objeto apuntado
por el apuntador apuntado por el apuntador b ms el valor del objeto apuntado por el apuntador a. La
pila lucira as:

1.5.3 Apuntadores y arreglos


El tema de los arreglos est ntimamente ligado al de apuntadores; tanto que es posible intercambiarlos
en la solucin de un problema.
El nombre
bre de un arreglo corresponde al de un apuntador que almacena un valor constante. Este valor
constante es la direccin de memoria del primer elemento del arreglo.

Por ejemplo :

int calif[ ]={100,90,95,80,90}; // Declaracin e inicializacin de un arreglo de 5


// enteros
enteros.
Se puede representar con la figura siguiente.
10

En realidad, el lenguaje manejar al arreglo a travs de un apuntador llamado calif, el cual tiene
almacenado el valor 65494, que a su vez corresponde a la direccin de inicio del elemento calif[0].

La representacin del apuntador calif, en la zona de variables globales de la memoria RAM, es la


siguiente :

Calif 65494

En el siguiente listado se presenta el manejo del arreglo calif[ ], a travs de la notacin de arreglos, y en
el listado subsiguiente llamado Progra11.cpp el manejo con la notacin de apuntadores.

Manejo de calif[ ] , notacin de arreglos

Progra10.cpp
#include <iostream.h>

void main()
{
int calif[ ] = { 100,90,95,80,90};

for(int i=0 ; i <5 ; i++) //Notacin de arreglos.


cout << "\n" << calif[i] ;
}

1.5.4 Notacin de apuntadores


De igual manera que la notacin arreglos se presenta el manejo de calif[ ] en notacin de apuntadores.
Progra11.cpp
#include <iostream.h>
void main()
{
int calif[ ] = { 100,90,95,80,90};
for(int i=0 ; i <5 ; i++) // Notacin de apuntadores
cout << "\n" << *(calif+i) ;
}
11

1.5.5 Anlisis de Progra11.cpp


Como puede observarse, la nica diferencia entre los listados llamados Progra10 y 11 es que el
primero utiliza calif[i] y el segundo *(calif+i).

Debido a que la ejecucin de los programas de ambos listados producen resultados iguales, se deduce
que:
calif[i] == *(calif+i)
Para entender esto que a simple vista no es obvio, revisaremos algunos conceptos:
1. El nombre del arreglo corresponde al de un apuntador que apunta al primer elemento del arreglo, por
lo que:
calif apunta a calif[0]
Visto grficamente:

2. Para hacer referencia a un elemento especfico del arreglo, se toma como base la direccin del
primer elemento y, con el subndice del elemento especfico, se calcula su direccin. Por ejemplo, para
referirse al segundo elemento del
el arreglo puede procederse as
as:

calif[1] // Notacin de arreglos

*(calif+1) // Notacin de apuntadores, donde la expresin calif+1sirve para calcular la direccin del
elemento que est una posicin ms all del elemento apuntado por calif.
Para referirse a calif[2] ( tercer elemento ), puede escribirse:
*(calif+2)
Lo que significa: "El objeto que se encuentra dos posiciones despus del objeto apuntado por calif". En
este caso, una posicin es el espacio requerido por cada uno de los elementos, de tal manera que, si
calif apunta a la direccin 65494, entonces calif+2 es la expresin que calcula la direccin 65498. La
figura muestra los elementos del arreglo calif[ ] con sus nombres en notacin de arreglos y en notacin
de apuntadores.

De lo anterior, se infiere la regla:


calif[i] == *(calif+i)
12

por lo que:
&calif[i] == calif+i

Esto es que, la direccin del i-simo elemento de un arreglo se calcula sumndole el subndice a la
direccin del primer elemento.

Como otro ejemplo, supongamos la siguiente declaracin correspondiente a un arreglo de 10 elementos


de tipo float.
float* sueldo[10];
Si la direccin del primer elemento es 65494, entonces:
&sueldo[5] es igual a :

sueldo+5 = 65494 + ( 5 * 4 ) = 65514

Como regla, podemos establecer que el subndice se refiere a la posicin del elemento en el arreglo. Es
por esto que al calcular la direccin por medio del subndice, ste debe multiplicarse por el nmero de
bytes que representan el tamao de cada elemento (dado por el tipo utilizado en la declaracin del
arreglo).

1.5.6 Operaciones con apuntadores

Se pueden realizar asignaciones entre punteros.


int a = 15;
int *p, *q;
q = &a;
p = q; /* se asigna la direccin que contiene q a p */
cout<<p; /* imprime la direccin 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;

En el siguiente cdigo se realiza un programa que emplea los operadores (& y *).

Progra12.cpp
#include <iostream.h>
#include <conio.h>
int main()
{
clrscr();
int a; //a es un puntero
13

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


a = 7;
ap= & a; //ap toma la direccin de a
cout <<"la direccin 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;
}

Capitulo 2. Gestin dinmica de memoria

2.1 Conceptos Bsicos de Memoria

Para iniciar este captulo es importante que le demos un vistazo a la memoria de la computadora. Si
usted ya sabe cmo funciona la memoria de la computadora, se puede saltar esta seccin. Sin
embargo, si no est seguro, le sugiero que la lea, le ayudar a comprender mejor ciertos aspectos de la
programacin.

La computadora usa memoria de acceso aleatorio (RAM) para guardar informacin mientras est en
funcionamiento. La RAM se encuentra en circuitos integrados o chips en el interior de la computadora.
La RAM es voltil, lo que significa que es borrada y reemplazada con nueva informacin tan pronto
como se necesita. La volatilidad tambin significa que la RAM recuerda solamente mientras la
computadora est encendida, y pierde su informacin cuando se apaga 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 un byte es la unidad de medida fundamental de la memoria de una computadora, de los
cuales se obtiene los Kilobytes, Megabytes, Gigabytes, siendo estos los ms usados. Un kilobytes de
memoria equivale a 1,024 bytes.

2.1.1 Tipo de datos y bytes requeridos en memoria

Para darse una idea de que tantos bytes se necesitan para guardar determinados tipos de datos lo
invito a que revise la siguiente tabla de espacios requeridos para guardar datos.

Datos Bytes requeridos


La letra X 1
El nmero 100 2
El nmero 125.123 4
La frase Aprenda usted mismo 21
Una pgina escrita completamente 3000 (Aproximadamente)

La RAM en la computadora est organizada en forma secuencial, un byte tras otro. Cada byte de
memoria tiene una direccin nica mediante la cual es identificado, una direccin que tambin lo
14

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 lmite del sistema.

Para ampliar un poco ms la conceptualizacin 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.

2.2 Tipos de datos comunes


Los tipos de datos ms comunes utilizados frecuentemente en C++ son:
TIPO DATO ESPACIO MEMORIA RANGO

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

2.2.1 Usaos de la Memoria RAM

Alguna vez nos hemos hecho 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 informacin 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 o cualquier otra cosa, la informacin (nombres, precios de acciones, gastos) es guardada
en la RAM de la computadora mientras el programa est ejecutando.
15

Hasta el momento, la mayora de los programas los hemos realizado definiendo variables, sin
preocuparnos de que se realiza internamente en el computador, muchas veces en forma indiscriminada,
es decir sin una verdadera depuracin, pero existen ocasiones en que no sabemos cuanta memora
necesitaremos para ejecucin de determinado programa, por ejemplo si deseamos realizar un
procesador de textos, no sabemos cul va hacer la longitud del texto.

Por eso a veces es necesario poder reservar memoria segn se va necesitando. Adems de esta forma
nuestros programas aprovecharn mejor la memoria del computador en el que se ejecuten, usando
slo los recursos necesarios.

Realmente la utilidad de asignacin dinmica de memoria ser aplicada en gran medida en los
captulos relacionados con las estructuras lineales.

De acuerdo a lo anterior podemos definir dos tipos de variables: estticas y dinmicas.

2.3 Tipos de variables

Dependiendo el uso que se le d a las variables por parte del programador, en una rutina o tarea
especfica se pueden identificar dos tipos de variables ellas son variables dinmicas y variables
estticas.

2.3.1 Variables estticas

Las variables estticas como recordamos en los inicios de los fundamentos de programacin, son
aquellas que el programador les asigna memoria antes de la ejecucin del programa o de una funcin,
las variables estticas se llaman mediante el nombre de la misma, que ha sido declarado por el
programador.

2.3.2 Aplicacin de las variables estticas

Veamos el cdigo de un programa que hace uso de las variables estticas

Progra13.cpp
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>

int priNumero; /* Nuestras variables */


int segNumero;
int suma;

void main()
{
clrscr();
priNumero = 136;
segNumero = 369;
suma = priNumero + segNumero;
16

cout<<"La suma es " <<suma;


getch();
}

2.3.3 Anlisis de Progra13.cpp

En el cdigo del programa se hace uso de tres variables de tipo entero que son variables estticas ellas
son: priNumero que se le asigna el valor 136, mientras 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.

2.4 Variables dinmicas


Las variables dinmicas deben su nombre al hecho de que pueden ser creadas y destruidas durante el
tiempo de ejecucin de un mdulo.

Para el manejo de variables dinmicas se hace indispensable la utilizacin de apuntadores, as como


de funciones especiales para la asignacin y liberacin de la memoria correspondiente a dichas
variables.
2.4.1 Aplicacin de las variables dinmicas

Veamos el cdigo de un programa que hace uso de las variables dinmicas


Progra14.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();
}

2.4.2 Anlisis de Progra14.cpp

En el listado del progra14.cpp se declaran tres variables apuntador de tipo entero ellas son *a, **b y **c
el apuntador a almacena la direccin de la variable x, mientras que **b precedida de dos asteriscos
indica que es una variable que apunta a un apuntador y ***c es un apuntador a apuntador a apuntador.
17

2.5 Asignar y liberar espacios en memoria

En el lenguaje C existen entre otras las funciones Malloc() y Free() para la asignacin y liberacin de
memoria dinmicamente respectivamente.

Cuando se ejecuta un programa, el sistema operativo reserva una zona de memoria para el cdigo o
instrucciones del programa y otra para las variables que se usan durante la ejecucin. A menudo estas
zonas son la misma zona, es lo que se llama memoria local. Tambin hay otras zonas de memoria,
como la pila, que se usa, entre otras cosas, para intercambiar datos entre funciones. El resto, la
memoria que no se usa por ningn programa es lo que se conoce como "heap" o montn.

Cuando nuestro programa use memoria dinmica, normalmente usar memoria del montn, y no se
llama as porque sea de peor calidad, sino porque suele haber realmente un montn de memoria de
este tipo.

Profundizando un poco en la asignacin dinmica, encontramos el operador sizeof, el cual determina el


tamao en bytes que se requiere en la asignacin dinmica 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 funcin con el propsito de determinar el tamao en bytes de
un parmetro.

2.5.1 Aplicacin del operador sizeof.

En el siguiente listado se evidencia la aplicacin y uso del operador sizeof en cada variable el cual
devuelve el nmero de bytes dependiendo del tipo de variable.

2.5.2 Implementacin del uso de sizeof

Progra15.cpp
#include <iostream.h>
#include <conio.h>
void main()
{
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;
int arreglo[20], * pt = arreglo;
clrscr();
gotoxy(20,2);
cout<<"valores utilizando sizeof para cada una de la varibles \n\n";
cout<<" variable c = " <<sizeof c;
cout<<"\t tipo char = " <<sizeof (char);
18

cout<<"\n variable s = " <<sizeof s;


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);
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();
return 0;
}

Captulo 3. Operadores y funciones en la gestin de memoria

3.1 Operadores New y Delete


El lenguaje C++ cuenta con dos operadores preconstruidos, ellos son: New y Delete, por esta razn no
se requiere incluir ninguna librera o archivo de cabecera para utilizarlos.

El operador New: Realiza una labor parecida a la de la funcin malloc(), asignando un bloque de
memoria segn sea requerido.

El operador Delete: Libera un bloque de memoria asignada por New en tiempo de ejecucin, de
manera semejante a como lo hace la funcin free().
La sintaxis para el uso del operador new es:

Apuntador = new tipo_de_dato;

Este operador hace una peticin al sistema operativo para que se le asigne un espacio de memoria, con
tamao de acuerdo al tipo de datos (recordemos la funcin sizeof), si este espacio est disponible, la
operacin regresa la direccin 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:

delete apuntador;

La ejecucin 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 cuando se usa memoria dinmica, 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. No os fiis de lo que
diga el compilador, de que estas variables se liberan solas al terminar el programa, no siempre es
verdad.
19

3.1.1 Aplicacin de los operadores New y Delete

Veamos un ejemplo de utilizacin de los operadores de c++ utilizados para asignar y liberar memoria
dinmicamente.

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

Progra16.cpp
# include <iostream.h>
main()
{
int index, *point1, *point2;
point1 = &index;
*point1 = 77;
point2 = new int;
*point2 = 173;
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;
c_point = new char;
delete c_point;
c_point = new char [sizeof(int) + 133];
delete c_point;
}

3.1.2 Anlisis de Progra16.cpp


El resultado de la ejecucin del listado llamado Progra16.cpp es:
Los valores son 77 77 173
Los valores son 77 999 999
En las primeras lneas del programa, se hace uso de los punteros tal y como se hacen tambin
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
localizacin del entero creado. Esta localizacin es asignada a point2. La siguiente lnea asigna
173 al entero al que apunta point2. Es importante distinguir entre point2, la localizacin del
20

entero, y *point2, el entero. El puntero point2 apunta ahora a una variable entera que se ha
reservado dinmicamente, y que puede utilizarse de igual forma que se haca en C. Como
ejemplo, se imprime el valor al que apunta.

A continuacin, se reserva memoria para una nueva variable, y point2 se refiere a la misma
variable reservada dinmicamente 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. Slo cuando se vuelva al sistema operativo se liberar la memoria que
ocupaba. Por tanto, no debe utilizarse.

Ya que el puntero point1 en s no ha cambiado, apunta realmente al dato original. Este dato
podra referenciarse otra vez utilizando point1, pero no es una buena prctica de programacin,
ya que no hay garanta de lo que el sistema pueda hacer con el puntero o el dato. La localizacin
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 slo puede utilizarse para liberar memoria
reservada con el operador new. Si se usa delete con cualquier otro tipo de dato, la operacin no
est definida, y por tanto nada sucede.

En el programa tambin 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 declaracin 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 tamao de un bloque
dinmicamente reservado, se muestra cmo reservar un bloque de tamao arbitrario. Esto es
posible utilizando la construccin de las ltimas lneas del programa, donde un bloque de 37
caracteres de tamao (37 bytes) es reservado. Un bloque de 133 bytes mayor que el tamao de
un entero se reserva posteriormente. Por tanto, el operador new se puede utilizar con la misma
flexibilidad de la funcin malloc() de C.

Cuando los datos reservados dinmicamente son borrados con delete, todava quedan en
memoria. Si repetimos la instruccin cout inmediatamente despus de utilizar delete, veremos
que todava se conservan los valores. Si la repetimos de nuevo antes de dejar el programa,
cuando el espacio que ocupaban debe haber sido sobre escrito, veremos que ya no es as.
Incluso aunque el compilador nos d los nmeros correctos, no es una buena prctica pensar
que esos datos estn ah todava, porque en un programa dinmico largo la memoria se usar
continuadamente.

Las funciones estndar utilizadas en C para manejo dinmico de memoria, malloc(), calloc() y
free(), tambin 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 cdigo C, lo mejor es continuar utilizando las funciones en
las nuevas lneas de programa. Si no es as, se deben utilizar los nuevos operadores, ya que se
21

han construido como parte del lenguaje en s, ms que aadirse, y por tanto son ms
eficientes1.

Cuando se utiliza new para reservar memoria para un vector, el tamao del vector se sita entre
corchetes, siguiendo al tipo:
int *intvector;
intvector = new int [20];
y se libera:
delete [ ] intvector;

3.2 Ms sobre la Implementacin de New y Delete

El siguiente muestra un ejemplo de aplicacin de los operadores new y delete.

Progra17.cpp

#include <iostream.h>
void main()
{
int *apent; // Declara un apuntador a entero
apent = new int ; // Reserva un bloque de memoria dinmica
// de 2 bytes para manejarlo por medio de apent.
*apent = 10 ; // Asigna el valor 10 al objeto apuntado por apent.
cout << *apent ; // Despliega el contenido del objeto apuntado por apent.
delete apent ; // Libera el espacio de memoria manejado por apent.
}
En el listado llamado Progra 17.cpp, se supone que la reservacin ser exitosa porque existe espacio
suficiente en el montculo.

Pero quin asegura que el espacio requerido por new est disponible?. Para controlar esta situacin y
evitar un mensaje de error por parte del sistema en tiempo de ejecucin, en el listado siguiente se
propone una nueva versin del programa.

3.2.1 Implementacin de New para verificacin de memoria

Progra18.cpp
#include <iostream.h>
#include <stdlib.h> // Para exit().

void main()
{
int *apent; // Declara un apuntador a entero

if((apent = new int)==NULL)//Intenta reservar un bloque de memoria dinmica


{ // de 2 bytes por medio de apent.
cout << "NO hay espacio suficiente\n";
exit(1); // Finaliza la ejecucin del programa.

1
Fuente http://www.pablin.com.ar/computer/cursos/c1/allocate.html
22

}
*apent="10" ; // Asigna el valor 10 al objeto apuntado por apent.
cout << *apent ; // Despliega el contenido del objeto apuntado por apent.
delete apent ; // Libera el espacio de memoria manejado por apent.
}

3.2.2 Anlisis de Progra18.cpp


Para crear un arreglo de 25 elementos de tipo double, en el montculo, puede escribirse:
Double *dap ;
dap = new double[25];
su forma equivalente:
double *dap = new double[25];
En este ejemplo, se est declarando a dap como un apuntador a variables dinmicas de tipo doble; al
tiempo que se le asigna el valor retornado por new. El valor retornado por new es la direccin del inicio
de un bloque de memoria del tamao requerido para almacenar 25 elementos de tipo double.

En caso de que el montculo no disponga del espacio requerido, new retorna el valor NULL ( nulo ).

3.3 Funciones Malloc() y Free()

3.3.1 La funcin Malloc()

Es una de las funciones de asignacin de memoria del lenguaje de programacin C (acrnimo 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 memoria del tamao pedido y regresa la direccin del primer
byte del bloque. No hay de qu preocuparse sobre que parte de memoria se usa, ya que esto es
manejado automticamente.

La funcin malloc () regresa una direccin, 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.

3.3.2 La funcin Free()

Al igual que malloc(), free() es una funcin del lenguaje de programacin C, utilizado para liberar la
memoria asignada por malloc (). Al usar la funcin 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 encabezado STDLIB.H

3.3.3 Ejemplos de aplicacin

Ejemplo 1
// asigna memoria para un arreglo de 50 enteros

Int *nmeros;
Nmeros = (int * ) malloc (50 * sizeof (int));
23

Ejemplo 2
// asigna memoria para un arreglo de 10 valores float

float *nmeros;
Nmeros = (float * ) malloc (10 * sizeof (float));

3.4 Aplicacin a la asignacin de memoria con Malloc() y Free()

Progra19.cpp
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
clrscr();
int *p==NULL;
int nbytes=100;
p=(int *)malloc(nbytes);
if(p=NULL)
{
cout<<"Insuficiente espacio en memoria\n");
return -1;
}
cout<<"se han asignado %d bytes de memoria\n", nbytes);
free(p);
getch();
}

El resultado del programa muestra un mensaje en pantalla confirmando que se realiz la asignacin
dinmica de memoria con xito.

Ahora veamos otro ejemplo de aplicacin de la asignacin dinmica de memoria utilizando las
funciones malloc () y free (), con un ingrediente adicional el uso de una estructura llamada persona que
nos permita almacenar el registro de una persona, dos tipos de datos diferentes, un dato de tipo
carcter que almacena el nombre de la persona y otro dato de tipo entero para almacenar la edad.

3.4.1 Implementacin del uso de Malloc() y Free()

Progra20.cpp
// Listado de libreras o archives de cabecera
#include<iostream.h>
#include<stdlib.h>
#include<conio.h>

// Definicin de la funcin principal


void main()
{
clrscr();
24

int n, i;

// Definicin de la estructura persona


struct persona
{
char nombre[20];
int edad;
};
// Definicin 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 dinmica a travs 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";
}
clrscr();
cout<<"LISTADO DE PERSONAS REGISTRADAS "<<"\n";

// El ciclo for usado para la impresin o visualizacin 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 funcin free () libera la memoria asignada al apuntador p


free (p);
}

3.5 Otras funciones para asignar memoria dinmica


Aparte de la funcin Malloc() tratada en la seccin anterior, utilizada para la gestin dinmica de
memoria, existen dos funciones adicionales para reservar memoria, ellas son: calloc() y realloc().

3.5.1 La Funcione Calloc()


A continuacin se presentan el prototipo para la definicin de la funcin Calloc().
void *calloc(size_t nmemb, size_t size);
25

Cuando se usa la funcin malloc() la memoria no es inicializada (a cero) o borrada. Si se quiere


inicializar la memoria entonces se puede usar la funcin calloc. La funcin calloc es
computacionalmente un poco ms cara pero, ocasionalmente, ms conveniente que malloc. Se debe
observar tambin la diferencia de sintaxis entre calloc y malloc, ya que calloc toma el nmero de
elementos deseados (nmemb) y el tamao del elemento (size), como dos argumentos individuales.
Por lo tanto para asignar a 100 elementos enteros que estn inicializados a cero se puede hacer:
int *ip;

ip = (int *) calloc(100, sizeof(int) );

3.5.2 La Funcin Realloc()


Esta funcin intenta cambiar el tamao de un bloque de memoria previamente asignado. El nuevo
tamao puede ser ms grande o ms pequeo. Si el bloque se hace ms grande, entonces el contenido
anterior permanece sin cambios y la memoria es agregada al final del bloque. Si el tamao se hace ms
pequeo entonces el contenido sobrante permanece sin cambios.
El prototipo de definicin para la funcin realloc() es como se presenta a continuacin.
void *realloc(void *ptr, size_t size);
Si el tamao del bloque original no puede ser redimensionado, entonces realloc intentar asignar un
nuevo bloque de memoria y copiar el contenido anterior. Por lo tanto, la funcin devolver un nuevo
apuntador (o de valor diferente al anterior), este nuevo valor ser el que deber usarse. Si no puede ser
reasignada nueva memoria la funcin realloc devuelve NULL. 2
Si para el ejemplo anterior, se quiere reasignar la memoria a 60 enteros en vez de 100 apuntados por
ip, se har;
ip = (int *) realloc ( ip, 60*sizeof(int) );

Referencias Bibliogrficas Unidad 1

KENNETH C, louden (2004). Lenguajes de programacin. Mexico D.F : Thomson.

AGUILAR, Luis (2003). Fundamentos de programacin, algoritmos, estructura de datos y


Objetos(tercera edicin). Espaa: McGRAW-HILL.

AGUILAR, Luis (2000). Programacin en C++, Algoritmos, estructura de datos y Objetos . Espaa:
McGRAW-HILL.

DEYTEL Y DEYTEL (1999). Como programar C++(segunda Edicin). Mexico D.F. : Prentice Hall.
McGRAW-HILL.

FARREL, Joyce (2000). Introduccin a la programacin lgica y diseo. Mexico D.F : Thomson.

http://www.programacionfacil.com/estructura_de_datos/manejo_de_memoria
http://www.conclase.net/c/fuentes.php?tema=3
http://www.ilustrados.com/publicaciones/EpZVVEZpyEdFpAKxjH.php

2
http://www.fismat.umich.mx/mn1/manual/node10.html consultado en Junio 18 de 2009

Você também pode gostar