Sie sind auf Seite 1von 16

Universidad Arturo Prat

Departamento de Ingeniera
Estructura de Datos

Nombre: Cristian Garviso


Profesor: Ceferino Castro
INDICE
PUNTEROS...................................................................................
3

Terminologa bsica ..........................................................3

Direcciones y
punteros.......................................................4

PUNTERO A PUNTERO.................................................................6

PUNTEROS A VOID......................................................................8

STRING Y
PUNTEROS...................................................................9

ASIGNACIN DINMICA DE MEMORIA......................................10

PUNTEROS A FUNCIONES..........................................................13

ARREGLOS DE TAMAO VARIABLE............................................15

CONCLUSIN.............................................................................1
6

2
PUNTEROS
El concepto de puntero est unido a la forma en que los tipos de
datos son almacenados en la memoria de un ordenador, ya que denotan
la direccin (address) o localizacin de una variable determinada. El
nombre de la variable determina el tipo (char, int, float o double) y su
direccin determina dnde est almacenada. Conocer la direccin de
una variable es importante porque:
Permite que las funciones cambien el valor de sus argumentos,
como veremos en el captulo siguiente.
Permite pasar vectores de forma eficiente entre funciones: en
lugar de copiar cada elemento del vector, se copia la direccin del
primer elemento.
Permite reservar memoria en tiempo de ejecucin en lugar de en
tiempo de compilacin, lo que significa que el tamao de un vector
puede ser determinado por el usuario en lugar de por el
programador.

El nombre de un array es un puntero al array. Por tanto, los punteros


y los arrays estn ntimamente ligados en C y en C++.

Terminologa bsica.

Para entender los punteros y los vectores, es necesario conocer


primero cmo se almacenan los nmeros en la memoria del ordenador.
El valor de cada variable en un programa de ordenador se guarda en
una seccin de memoria cuyo tamao est determinado por el tipo de
dato de la variable. La localizacin de esta seccin de memoria es
almacenada en la direccin de la variable. Por tanto, es como si cada
variable estuviera compuesta de dos partes: su valor y su direccin.
Cada celda de memoria se puede considerar compuesta de una parte
con el contenido, y otra en la que se almacena la direccin. Esto es
anlogo a una fila de casas: cada casa tiene diferentes contenidos, y
para mirarla necesitamos conocer su direccin.
Las direcciones se representan normalmente por un nmero

3
hexadecimal, pudiendo contener un carcter, un entero o un real
(aunque en realidad todos son almacenados como nmeros binarios).
Para obtener la direccin de una variable se utiliza el operador
direccin &:

#include <iostream.h>
main()
{
double d = 2.7183;
cout << "numero = " << d << "\tdireccin = " << &d << '\n';
}

El valor de d es 2.7183. El valor de &d es una direccin (donde


2.7183 est almacenado). La direccin es imprimida en formato
hexadecimal.

Direcciones y punteros

Un puntero guarda la direccin de un objeto en memoria, y como tal


un puntero es tambin una variable. Puede parecer algo confuso, es
como decir que el contenido de una casa es la direccin de otra
vivienda. Las direcciones se guardan como nmeros hexadecimales, por
lo que no hay ninguna razn por la que no podamos definir otro tipo de
dato, similar a un entero, pero a travs del cual se puede modificar el
valor almacenado en esa direccin. Es importante entender la relacin
entre punteros y direcciones:

Cada variable tiene su direccin, que puede ser obtenida mediante


el operador unario &.
La direccin de una variable puede ser almacenada en un tipo de
dato llamado puntero.

Un puntero en C o en C++ se declara anteponiendo un * al nombre


de la variable, que es el operador inverso a &. El puntero apunta
entonces a una variable del tipo especificado, y no debe ser usado con
variables de otros tipos. Un experto en C podra forzar la utilizacin de
un puntero con un tipo distinto del que se ha declarado, pero no es
recomendable, ya que podra conducir a un uso errneo.

Veamos algunos ejemplos de declaracin de punteros:

4
int *puntero1;
int *puntero2, *puntero3;
char variable, *punteroCaracter;
float *punteroReal, real;

En la primera lnea, declaramos un puntero a un entero. En la


segunda, dos punteros a entero. En la tercera, un carcter ( variable) y
un puntero a carcter (punteroCaracter). Por ltimo, punteroReal es un
puntero a un real, y real es declarado como un nmero real.

Veamos un ejemplo de uso del operador * y del operador de


direccin (&):

# include <iostream.h>

main ()
{
int *ptInt;
int var1 = 7, var2 = 27;
ptInt = &var1;
var2 = *ptInt;
cout << " var1 = " << var1 << " var2 = " <<var2 << "*ptInt = " <<*ptInt << '\n';
*ptInt = 5;
cout << " var1 = " << var1 << " var2 = " <<var2 << "*ptInt = " <<*ptInt << '\n';
ptInt = &*ptInt;
var1 = *&var1;
}

El resultado de la ejecucin es:

var1 =7 var2 = 7 *ptInt = 7


var1 = 5 var2 =7 *ptInt = 5

Estudiemos el funcionamiento del programa:

Se asigna a ptInt la direccin de var1. Entonces el valor de ptInt


es la direccin de memoria de var1.
A var2 se asigna el valor al que ptInt apunta. Entonces el valor de
var2 es el contenido de la localizacin de memoria la que ptInt
apunta (var1).
Despus de imprimir los tres valores por primera vez, el contenido
de la direccin a la que apunta ptInt es cambiado a 5. Como este
contenido es var1, el valor de var1 es ahora 5, pero var2 no es
modificado.
Las dos ltimas lneas ilustran cmo los operadores * y & son
conceptos inversos. Ntese que el orden de precedencia es de

5
izquierda a derecha, por tanto el ms cercano a la variable es
siempre el primero que se aplica.

PUNTERO A PUNTERO
Un puntero almacena la direccin de un objeto, puesto que ese
objeto puede ser otro puntero, es posible declarar un puntero que
apunta a puntero. La notacin de puntero a puntero requiere de un
doble asterisco, '**', la sola notacin suele generar un efecto de
confusin considerable, y es la razn de sugerir en lo posible reemplazar
punteros a punteros por alguna otra alternativa (una clase con miembro
puntero).

Sin embargo, como se vera, el concepto en si mismo no es


complejo. La relacin entre una variable comn, un puntero y un
puntero a puntero se muestra en las siguientes lneas:

int a = 4;
int* pt1 = &a;
int**pt2 = &pt1;

Por un lado tenemos el valor que almacena la variable 'a', el


puntero 'pt1' almacena la direccin de esa variable, y el puntero 'pt2'
almacena la direccin del puntero 'pt1'. Son tres identificadores, cada
uno tiene un doble aspecto: la localidad de memoria donde se asienta, y
el valor que almacena en esa localidad de memoria.

Declaracin e Direccin de memoria Valor que almacena en


inicializacin (hipottica) tal direccin de memoria

int a = 4; 0xfff6 4

int* pt1 = &a; 0xfff4 0xfff6

int**pt2 = &pt1; 0xfff2 0xfff4

Es interesante comprobar las diferentes salidas en pantalla de 'pt2'


en los siguientes casos:

6
cout<<pt2; //Imprime la direccion del propio puntero 'pt2', aqui: "0xfff2"
cout<<*pt2; //Imprime la direccion almacenada en 'pt2', "0xfff4"
cout<<**pt2; //Imprime el valor almacenado en '*pt1 = a', "4".

El comportamiento de la salida en pantalla es coherente, pues se


cumplen las siguientes igualdades:

*pt2 == pt1; //Desreferenciacion de 'pt2'


*(*pt2) == *(pt1); //Aplicamos '*' a ambos lados
*pt1 == a; //De esto y la linea previa se deduce...
**pt2 == a; //...esta igualdad

Lanse las anteriores lneas como 'igualdades' (comparaciones que


dan 'Verdadero') y no como asignaciones.

La estrecha relacin existente entre los conceptos de puntero y


array, es la razn de que el asterisco doble (**) pueda ser interpretado
indistintamente como puntero a puntero, o bien como un array de
punteros.

7
PUNTERO A VOID
El puntero a void es tambin un elemento del ANSI-C. A un
puntero a void puede asignarse el valor de cualquier puntero de otro
tipo. Por ejemplo, en el siguiente programa, al puntero general se le
asigna primero la direccin de un entero, y despus la de un nmero
real:

#include <iostream.h>

main ()
{
int *ptInt;
float *ptFloat;
int var1 = 7, var2 = 27;
float x = 1.2345, y = 32.14;
void *general;
ptInt = &var1;
*ptInt += var2;
cout << " var1 tiene ahora el valor \t" << *ptInt << '\n';
general = ptInt;
ptFloat = &x;
y += 5 * (*ptFloat);
cout << " y tiene ahora el valor \t" << y << '\n';
general = ptFloat;
}

La salida de este programa es:

var1 tiene ahora el valor 34


y tiene ahora el valor 38.3125

Esto permite al programador definir un puntero que puede ser


usado para apuntar a variables de tipos diferentes para transferir
informacin a lo largo del programa. Un buen ejemplo es la funcin

8
malloc(), que devuelve un puntero a void. Este puntero puede ser
asignado a un puntero de cualquier tipo, transfiriendo el puntero
devuelto al tipo correcto.
Un puntero a void se almacena en memoria de forma que puede
ser utilizado con cualquiera de los tipos simples predefinidos en C++,
pero tambin pueden ser utilizados con los tipos compuestos que el
usuario define, puesto que los tipos compuestos estn formados por
tipos simples.

STRING Y PUNTEROS
Se pueden leer datos de un string, y se puede enviar la salida a un
string en memoria. Para ello se debe incluir el fichero strstream.h y
declarar strings de entrada y salida. Un ejemplo de string de salida es el
siguiente:

#include <strstream.h>

main(){
char c[10];
ostrstream stringDeSalida(c,10);

stringDeSalida << "pi= " << 3.14 << endl;


cout << c;
}

que produce la siguiente salida por pantalla:

pi= 3.14

El string c se va llenando con el texto que hemos indicado. Si se


sobrepasa la dimensin que hemos declarado para c, entonces se dejar
de escribir caracteres en l.

El siguiente es un ejemplo de un string de entrada del cual leemos


datos:

#include <strstream.h>
#include <string.h>

main(){
char *s = "5.16 90 hola";
istrstream stringDeEntrada(s,strlen(s));

9
float f;
int i;
char t[10];

stringDeEntrada >> f >> i >> t;


cout << f << "\t" << i << "\t" << t<< "\n";
}

que produce en pantalla:

5.16 90 hola

ASIGNACIN DINMICA DE MEMORIA


Los operadores new y delete se utilizan para asignar y liberar
memoria dinmicamente, de forma similar a las funciones malloc() y
free() en C. Ahora los operadores new y delete son parte del lenguaje
C++ y no parte de una librera. El propsito de new es crear arrays
cuyo tamao pueda ser determinado mientras el programa corre.
delete funciona igual que free() en C. El contenido al que apunta el
puntero es borrado, pero no el puntero en s. Se pueden crear ms
variables y hacer que la localizacin sea el mismo puntero.
Veamos un ejemplo de utilizacin de estos operadores:

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

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

Resultado de la ejecucin:

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 haca 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 entero, y *point2, el
entero. El puntero point2 apunta ahora a una variable entera que
se ha asignado 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 asigna 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

11
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 asignada 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 asignado, se
muestra cmo asignar 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 asigna posteriormente. Por tanto, el operador
new se puede utilizar con la misma flexibilidad de la funcin
malloc() de C.
Cuando los datos asignados 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 sobrescrito, 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 han construido como parte del
lenguaje en s, ms que aadirse, y por tanto son ms eficientes.
Cuando se utiliza new para asignar memoria para un vector, el
tamao del vector se sita entre corchetes, siguiendo al tipo:

12
int *intvector;
intvector = new int [20];

y se libera:

delete [] intvector;

PUNTEROS A FUNCIONES
Los punteros a funciones ya existan en C, aunque no se utilizan
regularmente. Veamos un ejemplo:

#include <iostream.h>

void printMensaje (float dato);


void printNumero (float dato);
void (*funcPuntero)(float);
main()
{
float pi = 3.14159;
printMensaje(pi);
funcPuntero = printMensaje;
funcPuntero (pi);
funcPuntero = printNumero;
funcPuntero (pi);
printNumero(pi);
}
void printMensaje(float dato)
{
cout <<" Esta es la funcion printMensaje " <<'\n';
}
void printNumero(float dato)
{
cout <<" Este es el dato: " << dato << '\n';
}

La salida del programa ser:

13
Esta es la funcion printMensaje
Esta es la funcion printMensaje
Este es el dato: 3.14159
Este es el dato: 3.14159

Hemos declarado dos funciones, printMensaje y printNumero, y


despus funcPuntero, que es un puntero a una funcin que recibe un
parmetro real y no devuelve nada (void). Las dos funciones declaradas
anteriormente se ajustan precisamente a este perfil, y por tanto pueden
ser llamadas por este puntero.

En la funcin principal, llamamos a la funcin printMensaje con el


parmetro pi, y en la lnea siguiente asignamos al puntero a funcin
funcPuntero el valor de printMensaje y utilizamos el puntero para llamar a la
misma funcin de nuevo. Por tanto, las dos llamadas a la funcin
printMensaje son idnticas gracias a la utilizacin del puntero funcPuntero.

Dado que hemos asignado el nombre de una funcin a un puntero


a funcin, y el compilador no da error, el nombre de una funcin debe
ser un puntero a una funcin. Esto es exactamente lo que sucede. Un
nombre de una funcin es un puntero a esa funcin, pero es un puntero
constante que no puede ser cambiado. Sucede lo mismo con los
vectores: el nombre de un vector es un puntero constante al primer
elemento del vector.

Ya que el nombre de una funcin es un puntero a esa funcin,


podemos asignar el nombre de una funcin a un puntero constante, y
usar el puntero para llamar a la funcin. Pero el valor devuelto, as como
el nmero y tipo de parmetros deben ser idnticos. Muchos
compiladores de C y C++ no avisan de las diferencias entre las listas de
parmetros cuando se hacen las asignaciones. Esto se debe a que las
asignaciones se hacen en tiempo de ejecucin, cuando la informacin de
este tipo no est disponible para el sistema.

Si en el ejemplo anterior aadimos una funcin que tiene como


parmetro un entero e intentamos llamarla con el puntero a funcin
funcPuntero, el compilador lanzar el siguiente mensaje de error:

error: In this statement, the referenced type of the pointer value "print_int" is
"function (int) returning void", which is not compatible with "function (float) returning
void". Compilation terminated with errors.

14
ARREGLOS DE TAMAO VARIABLE
Mediante el uso de los operadores new y delete se puede
determinar el tamao de un array en tiempo de ejecucin. La lnea:

int *const p = new int[n];

declara p como un puntero constante a un entero. El operador new crea


un array de n objetos de tipo entero y devuelve un puntero al primer
elemento del array. Adems, p es inicializado a este puntero.

Veamos cmo se puede utilizar una definicin de este tipo a la


hora de determinar el tamao de un vector durante la ejecucin de un
programa. El siguiente ejemplo lee del teclado el tamao de un vector y
sus componentes y despus lo imprime de nuevo:

#include <iostream.h>

main(){
int n;
cin >> n;
float* const p = new float[n];

for(int i=0; i<n; i++)


cin >> p[i];

for(int i=0; i<n; i++)


cout << p[i] << '\t';
}

15
CONCLUSIN
En estas lneas nos hemos dedicado a aprender y
comprender un poco mas lo que son los punteros, sus distintas
clases y usos, y se ha visto como con su implementacin se
pueden realizar distintos tipos de algoritmos, facilitando el uso
de memoria y haciendo mas dinmicos los programas realizados
con su uso.

Nos hemos dado cuenta que sin ellos seria casi imposible
en C y C++ realizar ciertas aplicaciones que requieren un manejo
excesivo de memoria, dando la oportunidad de estructurar los
algoritmos realizados con el fin de que el programador no tenga
que asignar completamente la memoria al realizar el programa y
que sea el usuario final quien a medida que va utilizando la
aplicacin valla pidindole a su computador la memoria que se
va a utilizar sin que este se de cuenta.

16

Das könnte Ihnen auch gefallen