Sei sulla pagina 1di 20

Universidad Nacional del Litoral

Facultad de Ingeniería y Ciencias Hídricas


Departamento de Informática

Ingeniería Informática
PROGRAMACIÓN ORIENTADA
A OBJETOS

UNIDAD 1
Punteros
Ing. Horacio Loyarte
® 2009
Unidad 1 2

Unidad 1
Punteros

Resumen de Conceptos

Introducción
Un puntero es una variable que representa un valor numérico correspondiente a la
ubicación física de un elemento determinado del programa. Ese valor numérico
asignado a un puntero también es conocido como dirección de memoria del ele-
mento. Dicho elemento puede constituir en C++:
• Un dato simple
• Una estructura de datos
• Una función
• Una variable de cualquier tipo

Es decir que podemos emplear punteros para referenciar datos o estructuras más
complejas y también administrar bloques de memoria asignados dinámicamente. Su
empleo hace más eficiente la administración de recursos del programa y su ejecu-
ción.

Muchas funciones predefinidas de C++ emplean punteros como argumentos e in-


clusive devuelven punteros. Para operar con punteros C++ dispone de los operado-
res & y * .

Una de las ventajas más interesantes del uso de punteros es la posibilidad de crear
variables dinámicas; esto es, variables que pueden crearse y destruirse den-tro de
su ámbito, lo cual permite optimizar el uso de recursos disponibles en un programa.

Definición de Punteros
Para declarar un puntero, C++ emplea el operador * anteponiéndolo a la variable
puntero. Además se debe indicar el tipo de dato al que apuntará esta variable.
tipo *variable_puntero;

Ejemplo:
int *x; // se declara a x como variable puntero a un entero

dirección de
la variable dato

*x
Dato apuntado
por x
FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 3

Variable de x
tipo puntero
En el ejemplo anterior, la variable puntero x contiene la dirección donde se alma-
cenará un dato de tipo entero, pero *x es una expresión nemotécnica que corres-
ponde a un entero; por lo tanto podemos emplear *x en cualquier proposición o
expresión de C++ que requiera la presencia de un entero.
En otras palabras: *x es una expresión que representa un valor entero almacena-
do en la dirección de memoria contenida en x.

Relación entre los operadores de referencia &


y de desreferencia *
Como vimos en el epígrafe anterior, es posible declarar una variable puntero a tra-
vés del operador * y asignarle la dirección de memoria de algún elemento del pro-
grama.

Pero si declaramos una variable simple cualquiera: ¿Cómo determinamos su direc-


ción?. Como ya estudiamos, en C++ es posible conocer la dirección de cualquier
variable a través del operador dirección que representamos con: &. Ese dato solo
podremos asignarlo a una variable que admita direcciones, es decir a una variable
puntero:

int z=8; // declaramos e inicializamos con 8 una variable entera z


int *p; // declaramos un puntero p a un entero
p= &z; // asignamos la dirección de z al puntero p
cout<< *p <<endl; // informamos 8, el dato apuntado por p
cout<< p; // informamos la dirección de memoria contenida en p

Nota: la asignación de la dirección de z a p fue posible pues z es entero y p fue de-


clarada como puntero a un entero.

En resumen: el operador & seguido de un operando permite obtener la dirección de


dicho operando; por el contrario, el operador * permite obtener el dato ubicado en la
dirección asignada a una variable puntero. De allí que se denomine a estos opera-
dores:
&: operador dirección o referencia
*: operador indirección o desreferencia;

La constante NULL
Si una variable puntero no ha sido inicializada apunta a una dirección aleatoria. Si la
lógica del programa requiere que una variable puntero no apunte a ningún dato ni
elemento del programa podemos asignarle la constante NULL.
float *q = NULL;
cout << q; //devuelve la dirección nula 00000000
El objeto de definir con NULL a una variable puntero tiene que ver con controles que
suelen ser útiles en algoritmos que emplean estructuras de datos con punteros.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 4

Nota: la mayoría de los compiladores C++ devuelven un entero en base hexadeci-


mal de 8 cifras para definir una dirección de memoria.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 5

Variables dinámicas: new y delete


Al declarar una variable puntero a un float escribimos: float *q;
Esto significa que el compilador de C++ reserva memoria para el puntero q, donde
este almacenará la dirección de inicio de un dato flotante. Pero esa dirección aún
no ha sido alocada con un dato y podría suceder que esté siendo usada por alguna
otra variable. Si intentamos acceder a la dirección que este apunta se producirá un
error.

float *q=3.14159;//ERROR:no storage has been allocated for *q


(//ERROR: No hay almacenamiento reservado para ubicar a *q)

Vimos que una forma adecuada de evitar tal error es la siguiente:


float x=3.14159; // x almacena el valor 3.14159
float *q=&x ; // q contiene la dirección de x
cout<<*q ; // OK: *q ha sido convenientemente ubicado

En este caso no habrá inconveniente para acceder a *q porque previamente se


creó la variable x, y luego se le dio a q la dirección de x.
Otra forma de resolver la asignación de un espacio de memoria para definir un pun-
tero es destinar la memoria necesaria para almacenar el dato que será apuntado
por el puntero en cuestión empleando el operador new:

float *q; //se declara el puntero a flotante q


q= new float; //reserva un espacio de almacenamiento para *q
*q=3.14159; // OK: q y *q se han inicializado sin error

Se pueden combinar las líneas anteriores en una sola:

float *q=new float(3.14159);

Esto permite ubicar un dato en memoria en tiempo de ejecución. También en tiempo


de ejecución se puede efectuar la operación delete inversa a new, desalocando
la memoria apuntada y dejándola disponible para que el programa la emplee para
almacenar otro dato.

delete q; //libera el espacio de memoria apuntada por q

Operaciones con Punteros


Las variables puntero pueden ser operadas aritméticamente; esto nos permite refe-
renciar una nueva dirección de memoria. Esa nueva dirección dependerá del tipo de
dato al que apunta la variable puntero. Por ejemplo:
int *p;
p+=3; /*La nueva dirección de p supera a la anterior en 12 bytes, pues al sumar 3
estamos desplazándonos la cantidad de bytes correspondientes a 3 enteros (12 by-
tes) */
double *r;

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 6

r+=2; /*La nueva dirección de r supera a la anterior en 16 bytes pues al incremen-


tar en 2 su dirección, la estamos desplazando la cantidad de bytes correspondien-
tes a 2 datos de tipo double (16 bytes)*/

Ampliando estos conceptos podemos resumir las siguientes operaciones como vá-
lidas para las variables de tipo puntero:
a) Se puede asignar a una variable puntero la dirección de una variable no pun-
tero.
float x, *p;
.....
p=&x;
b) A una variable puntero puede asignarse el contenido de otra variable puntero
si ambas variables son compatibles (ambos punteros apuntan al mismo tipo
de dato).
int *u, *v;
.....
u=v;
c) A un puntero es posible asignarle el valor NULL (el puntero no apunta a direc-
ción de memoria alguna).
int *p;
p=NULL; //Dirección nula: 00000000
d) Es posible sumar o restar una cantidad entera n a una variable puntero. La
nueva dirección de memoria obtenida difiere en una cantidad de bytes dada
por: n por el tamaño del tipo apuntado por el puntero.
int *p;
.....
p+=4; //la dirección original de p se ha incrementado 16
bytes
p-=1; //La dirección anterior de p se decrementó en 4
bytes.
e) Es posible comparar dos variables puntero.
u<v u>=v u==v u!=v u==NULL
f) Una variable puntero puede ser asignada con la dirección de una variable
creada dinámicamente a través del operador new.
float *q; // se declara q como puntero a un float
q= new float; /* se asigna a q la dirección de una nueva
variable */
*q=4.1513; //se almacena un float en la dirección de q

Paso de punteros a una función


El objeto de pasar punteros a una función es poder modificar los parámetros de lla-
mada, es decir, permite efectuar un pasaje por referencia.

Al pasar un puntero como parámetro por argumento de una función se pasa real-
mente la dirección del argumento; cualquier cambio que se efectúe en los datos ubi-
cados en esa dirección, se reflejarán en el bloque de llamada y en la propia función.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 7

Esto es radicalmente diferente al pasaje de parámetros por valor donde las direccio-
nes de los argumentos de llamada son diferentes a las direcciones de los argumen-
tos formales.
Para observar la diferencia entre los diferentes pasajes de parámetros, analicemos
primeramente un ejemplo donde apliquemos el pasaje de parámetros por valor:

/*** ejemplo ***/


#include <iostream.h>
#include<conio.h>

void f_porvalor(int x,int y);


Llamada a la función
f_porvalor donde los
parámetros actuales
void main( )
son pasados por {
valor. int dato1=5,dato2=10;
cout<<"Datos iniciales: dato1="<<dato1<<" dato2="<<dato2<<"\n";
cout<<"\n Pasaje por valor \n";
f_porvalor(dato1, dato2);
cout<<"Despues de llamar a la funcion
f_porvalor(dato1,dato2):\n”;
cout<<"Datos iniciales: dato1="<<dato1<<" dato2="<<dato2<<"\n";
getch();
}
void f_porvalor(int x,int y)
{ x=x*100; y=y*100;
cout << "Dentro de la funcion: x="<<x<<" y="<<y<<"\n";
}
La salida de este programa será
Datos iniciales: dato1=5 dato2=10
Pasaje por valor
Dentro de la función: x=500 y=1000
Después de llamar a la función f_porvalor(int x, int y)
Datos iniciales: dato1= 5 dato2=10

Obsérvese que los datos iniciales no se ven alterados en main().


En el ejemplo siguiente se plantea un caso similar, pero pasando parámetros a tra-
vés de punteros:
/*** ejemplo ***/
#include <iostream.h>
#include <conio.h>
Llamada a la función
void f_porpunteros(int *p,int *q); f_porpunteros donde los
parámetros actuales son
void main( ) pasados por referencia.
{ int dato1=5,dato2=10;
cout<<"Datos iniciales: dato1="<<dato1<<" dato2="<<dato2<<"\n";
cout<<"\n Pasaje por referencia \n";
f_porpunteros(&dato1, &dato2);
cout<<"Despues de llamar a la funcion f_porpunteros(int *p,int*q);
cout<<"dato1="<< dato1<<" dato2="<<dato2<<"\n";
getch();
}
void f_porpunteros(int *p,int *q)

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 8

{ *p+=45; *q+=60;
cout << "Dentro de la funcion: *p="<<*p<<" *q="<<*q<<"\n";
}

Datos iniciales: dato1=5 dato2=10


Pasaje por referencia
Dentro de la función: *p=5 *q=10
Después de llamar a la función f_porpunteros(int *p, int *q)
Datos iniciales: dato1= 50 dato2= 70

En este caso los parámetros actuales dato1 y dato2 se han modificado pues en la
función se ha trabajado con sus direcciones.

En el caso de pasaje de un parámetro de tipo array a una función debemos consi-


derar que el nombre de un arreglo representa a la dirección del primer elemento del
arreglo. Por esto, no es necesario emplear el símbolo & al llamar a una función con
un parámetro actual de tipo arreglo.
int x[6]={5,9,12,45,41,11};
func(x); /*Llamada a una función empleando como parámetro la
dirección de inicio del arreglo x, es decir: &x[0] */

Punteros a arreglos lineales


En C++ los punteros y el tipo arreglo están íntimamente relacionados. Las declara-
ciones de arreglos que hemos estudiado pueden plantearse a través de punteros lo-
grando mayor eficiencia en la ejecución del programa.

Como dijimos, el nombre de un arreglo representa la dirección del primer elemento


del arreglo, es decir, el nombre es un puntero al inicio de esa estructura.

int x[6]={5,9,12,45,41,11};

Esto significa que en el arreglo lineal x del ejemplo, la dirección de su primer


componente puede ser referenciada con el propio identificador del arreglo x, o
también con &x[0].

Para referenciar al segundo elemento del arreglo podemos hacerlo de 2 formas


equivalentes: &x[1] y x+1.

cout << x+1 << endl; /* obtenemos como salida la dirección


del elemento 1 del arreglo. Por ejemplo: 0000FFEE */
cout << &x[1] << endl; /* otra forma de obtener como salida
la dirección del elemento 1 del arreglo: 0000FFEE */

O sea que, la dirección del elemento i-ésimo de un arreglo x será x+i-1 o bien
&x[i-1].

Para expresar el contenido del elemento i-ésimo de un arreglo, podemos emplear


también dos formas: x[i-1] y *(x+i-1)

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 9

Observemos el siguiente programa C++ donde se obtienen y muestran en la salida


las direcciones de memoria de cada componente del arreglo x :
#include <iostream.h>
void main()
{
int x[]={12,13,14,15,16};
for (int i=0; i<5; i++)
cout<<&x[i]<<endl;
}

La salida que se obtiene es similar a la siguiente:


O012FF78
O012FF7C
O012FF80
O012FF84
O012FF88

Obsérvese que las direcciones de memoria de los 6 componentes del arreglo x (en
base hexadecimal) saltan de 4 en 4. Esto es debido a que cada elemento del arre-
glo es de tipo int y requiere 4 bytes de memoria. Si el arreglo fuera de elementos de
tipo float las direcciones también saltarán de 4 en 4 (cada número de tipo float ocu-
pa 4 bytes). Para el tipo double el salto sería de 8 bytes.

Obsérvese a continuación 4 maneras distintas de asignar el sexto y último elemen-


to del arreglo x al segundo elemento de dicho arreglo:
x[1] = x[5] ;
x[1] = *(x+5) ;
*(x+1) = x[5] ;
*(x+1) = *(x+5) ;
Entonces, de acuerdo con el concepto de que el nombre de un arreglo representa
a la dirección de su primer elemento, podemos declarar al arreglo lineal x de la ma-
nera siguiente

int *x;
La diferencia con la declaración int x[6]={5,9,12,45,41,11}; se basa en
que *x no reserva un espacio de memoria para todos los elementos del arreglo.
Es necesario definir un bloque de memoria para alojar la estructura. Esto es. posi-
ble si usamos las funciones de biblioteca de C: malloc() y sizeof().
x = (int *) malloc(6*sizeof(int));
La expresión anterior asigna a x un puntero a un bloque de memoria cuya cantidad
de bytes está indicada en el argumento (24 bytes en el ejempo). La fusión de tipos
(int *) es necesaria pues la función malloc() es de tipo void. De este modo
reservamos la cantidad de bytes requeridas en un bloque único de memoria para al-
macenar el arreglo de 6 elementos enteros.

Importante: si en la declaración de un arreglo se incluye la incialización entre llaves


de los elementos del mismo, se lo tiene que declarar como arreglo y no se puede
emplear el operador *.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 10

/* Ejemplo: generar aleatoriamente un arreglo de 20 números natu-


rales menores que 1000. Luego insertar en la posición 5 (sexto ele-
mento) el valor -45. El programa debe mostrar el nuevo arreglo de
21 elementos */
#include <stdlib.h>
#include <alloc.h>
#include <iostream>
#include <iomanip.h> Reserva de memoria para 21
using namespace std; elementos enteros del arreglo x

void main()
{
int *x,i;
x=(int*) malloc( 21*sizeof(int) );
En el for se genera y muestra el
for(i=0; i<20; i++) arreglo de enteros aleatorios
{ *(x+i)=rand()%(1000);
cout<<setw(8)<< *(x+i); }

for (i=20; i>5; i--) Se desplazan los elementos para poder usar la
*(x+i)=*(x+i-1); posición 5 e insertar el nuevo dato.

*(x+5)=-45;
Se inserta el dato -45 en la posición 5 del array.
cout<<endl<<endl;
for( i=0; i<21; i++)
cout<<setw(8)<< *(x+i);
Se muestra el nuevo arreglo de 21 componentes.
} // fin del programa

Punteros a Arreglos Multidimensionales


Consideremos el caso de un arreglo bi-dimensional o matriz. Podemos considerarlo
como una lista de arreglos unidimensionales (lista de filas por ejemplo). Si tenemos
en cuenta que un arreglo uni-dimensional se define con un puntero a su primer ele-
mento, una lista de arreglos unidimensionales para plantear una matriz puede defi-
nirse empleando punteros.

Supongamos que deseamos operar con una matriz de 6 filas por 7 columnas. Para
ello declararemos dicha estructura empleando un puntero a una colección de 6
arreglos lineales contiguos de 7 elementos cada uno:
int (*z)[7]; //en lugar de declarar z[6][7]

z 56 78 65 64 90 81 77 Primer arreglo lineal

z+1 11 10 15 18 16 12 14 Es muy importante


Segundo la
arreglo lineal
presencia del parénte-
Puntero al inicio z+2 43 40 41 67 44 45 62 sis (*z), pues de otro
del 3er arreglo
z+3 101 190 87 105 123 114 79
FICH - UNL
Puntero al inicio
Programación z+4
Orientada a Objetos-2009 23 27 39 21 20 28 100
del 5to arreglo
z+5 66 68 33 72 31 60 99 Sexto arreglo lineal
Unidad 1 11

modo, estaríamos definiendo un arreglo de punteros. Esto está de acuerdo con los
operadores asterisco y corchetes que se evalúan de derecha a izquierda.

Estudiemos cómo acceder a los elementos de esta matriz, definida a través de una
colección de arreglos lineales contiguos y empleando un puntero z al primer ele-
mento del primer arreglo.

Tomemos el caso de la fila 4 (quinta fila)


z+4 23 27 39 21 20 28 100
Puntero al inicio de la
quinta fila (5to arreglo)

*(z+4) *(z+4)+6
Dirección del ele- Dirección del
mento [4,0] elemento [4,6]
Como z+4 es un puntero a la quinta fila *(z+4) será el objeto apuntado. Pero el
objeto apuntado resulta ser un arreglo lineal de 7 elementos y por tanto *(z+4)
hace referencia a toda la fila; por ello para identificar a los elementos de esta fila y
acceder a los datos debemos desreferenciar las direcciones de estos punteros:

z+4 23 27 39 21 20 28 100
Puntero al inicio de la
quinta fila (5to arreglo)

dirección: *(z+4)
dato: *(*(z+4))

dirección: *(z+4)+1 dirección: *(z+4)+5


dato: *(*(z+4)+1) dato: *(*(z+4)+5)
)

Recordemos que el nombre de un arreglo es un puntero a su primer elemento. Por


eso *(z+4) es un puntero al inicio de la quinta fila, pero a su vez es el nombre de
un arreglo lineal: la quinta fila. Por ello necesitamos desrefreneciar la dirección con
el operador * para obtener el dato.
En general, para operar con un arreglo bidimensional definido a través de un punte-
ro z al inicio de una serie de arreglos lineales podemos decir:
Para indicar el puntero al inicio de la fila i: *(z+i)
Para identificar un puntero al elemento [i][j]: *(z+i)+j
Para identificar el dato ubicado en la fila i, columna j: *(*z+i)+j)

//Ejemplo del uso de punteros para acceso a arreglos;

#include <iostream>

using namespace std;

int main(){
const int filas=6;
const int columnas=7;
int a[filas][columnas];
int i,j;

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 12

for (i=0;i<filas;i++)
for (j=0;j<columnas;j++){
a[i][j]=random()%100;
cout << a[i][j] << " ";
}

cout << endl << endl;


int (*x)[columnas];

x=&a[0];
for (i=0;i<filas;i++)
for (j=0;j<columnas;j++)
cout << *((x[i])+j) << " ";
return 0;
}

Punteros y const
Empleando la palabra reservada const podemos declarar como constante a:
i) una variable un puntero, o bien a: ii) el objeto apuntado.

Declaración del puntero p como constante


int dato=25;
int *const p=&dato;

Para definir una constante apuntado por un puntero q


float valor=1.89;
const float *q=&valor

Es posible que tanto el puntero como el objeto apuntado sean constantes:


const char *const r="Universidad";

Arreglos de Punteros
Es posible definir un arreglo en C++ cuyos elementos contengan direcciones de
memoria en lugar de datos. Un arreglo de este tipo constituye un array de punte-
ros. La sintaxis que debemos emplear en C++ es la siguiente:
int *p[20];

En este caso p[0] es el primer puntero y apunta a una entero; p[1] es el siguiente
puntero que apunta a otro entero, hasta p[19] que constituye el último puntero.

Esta declaración puede ser útil para representar una matriz de 2 dimensiones. Pero
en este caso debemos reservar memoria para cada una de las filas. Por ejemplo si
deseamos representar la matriz de enteros de 6 filas por 7 columnas (que usamos

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 13

antes) empleando un arreglo de punteros, tal reserva de espacio debe indicarse


como sigue:

int *p[6]; //cantidad de punteros (filas)


for (int fila=0; fila<6; fila++)
p[fila]=(int *) malloc(7*sizeof(int));
// reservamos espacio para 7 datos enteros por cada fila

Primer puntero p[0] 56 78 65 64 90 81 77 Primer arreglo lineal


del arreglo
p[1] 11 10 15 18 16 12 14 Segundo arreglo lineal
3er elemento del
p[2] 43 40 41 67 44 45 62
arreglo de punteros
p[3] 101 190 87 105 123 114 79

p[4] 23 27 39 21 20 28 100

p[5] 66 68 33 72 31 60 99 Sexto arreglo lineal

En este caso, para mostrar el elemento de la fila 4 columna 5 podemos hacer:


cout << *(p[4]+5);
Donde p[4] es un puntero al primer elemento de la fila 4. Al sumar 5 a ese punte-
ro (p[4]+5) estamos haciendo referencia a la dirección (puntero) del sexto ele-
mento de esa fila. El dato almacenado ene esa dirección se obtiene a través del
operador de indirección: *(p[4]+5)

p[4] 23 27 39 21 20 28 100

*(p[4]+5) (p[4]+5)

La función free( ) permite liberar el espacio reservado por malloc(). Requiere


como argumento la variable puntero que tiene la dirección donde se inicia el bloque.

//Arreglo de punteros

#include <iostream>

using namespace std;

int main(){
const int filas=6;
const int columnas=7;
int *p[filas];
int i,j;

for(i=0;i<filas;i++)

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 14

p[i]=(int*)(malloc(columnas*sizeof(int)));

for (i=0;i<filas;i++)
for (j=0;j<columnas;j++)
*(p[i]+j)=i*10+j;

for (i=0;i<filas;i++)
for (j=0;j<columnas;j++)
cout << *(p[i]+j) << " ";

for (i=0;i<filas;i++)
free(p[i]);

return 0;
}

Punteros y Funciones
Como en el caso de arreglos el nombre de una función representa la dirección de
memoria donde se localiza dicha función. Por lo tanto ese puntero (a la función)
puede emplearse y operarse como cualquier puntero a datos simples o a estructu-
ras.
Ya estudiamos que un puntero puede pasarse como argumento de una función, lo
que en este caso será pasar una función como argumento de otra función.
Al plantear un identificador de función como parámetro de otra función, debe inter-
pretarse como un parámetro de tipo puntero (puntero a la función argumento) que
de hecho es usado como si fuera una variable. La expresión: int (*f)();
define a f como un puntero a una función que retorna un entero. Nótese la
presencia de paréntesis, obligatorios que rodean a *f. Sin ellos, la expresión int
*f(); estaría definiendo una función que retorna un puntero a un entero.

Luego de dicha declaración podemos decir que *f es la función y f un puntero a di-


cha función. Esto es muy similar al caso de arreglos, donde el nombre del arreglo
era un puntero al inicio de dicha estructura.

#include <iostream.h>
#include <stdio.h>
#include <math.h>
Puntero a la función
#include<conio.h>
argumento
void complejas(void) //función argumento
{ puts("Raices complejas");}

void resolvente( int x,int y,int z, void (*p)(void) )


{ float dis,r1,r2;
dis= y*y-4*x*z;
if (dis <0) Llamada a la función
p(); argumento (complejas)
else

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 15

{ r1=(-y+sqrt(dis))/(2*x);
r2=(-y-sqrt(dis))/(2*x);
cout<<"r1="<<r1<<endl;
cout<<"r2="<<r2; }
}

void main(void)
{ int a,b,c;
cout<<"Ecuacion de 2do grado\n";
cout<<"a=";cin>>a;
cout<<"b=";cin>>b;
cout<<"c=";cin>>c;
resolvente(a,b,c,complejas);//Llamada a función resolvente
}

Cuál es la ventaja de emplear punteros a funciones?


Una utilidad muy interesante de emplear punteros a funciones es el hecho de poder
invocar a una función empleando como parámetro funciones diferentes en cada lla-
mada. Veamos esto en un ejemplo.

La función sum tiene 2 parámetros: el primero es un valor entero que será acumula-
do tantas veces como indique el siguiente parámetro. El primer argumento lo repre-
sentaremos mediante una función que devuelve un entero y acepta como argumen-
to otro entero.
int sum(int (*pf)(int a),int n); //función que suma n veces un
entero
El siguiente programa C++ emplea sum para informar la suma de los cuadrados
(1+4+9+16) y de los cubos (1+8+27+64) de los primeros 4 números naturales
int sum(int (*pf)(int a),int n);
{ int acum=0;
for (int i=1;i<=n;i++)
acum+=(*pf)[i]
return acum; }

int cuad(int a) {return a*a;}

int cubo(int a) {return a*a*a;}

void main(void)
{ cout<<sum(cuad,4)<<endl;
cout<<sum(cubo,4)<<endl;
}

En conclusión podemos afirmar que un puntero a una función es un puntero que re-
presenta la dirección del nombre de la función- Pero como el nombre de una fun-
ción es a su vez un puntero, decimos que un puntero a una función es un puntero a
una constante puntero.
int f(int x); //declaración de la función f
int (*pf)(int x);//declaración del puntero pf a función
pf=&f; //asignamos la dirección de f al puntero pf


pf 
f int f(int
n)
FICH - UNL
{
Programación Orientada a Objetos-2009 .....
Unidad 1 16

Arreglos dinámicos
Estudiamos que el nombre de una arreglo es un puntero alocado en tiempo de com-
pilación,

float x[50]; // arreglo estático tradicional

La declaración siguiente define un arreglo dinámico creado en tiempo de ejecución.

float *q new float[50]; // arreglo dinámico

Al igual que free libera la memoria asignada con malloc, disponemos en C++ del
operador delete para liberar la memoria asignada por new.

delete q; // libera la memoria apuntada por q

Actividades

Ejercicios
1  Explique el significado de las siguientes expresiones de código C++.

a) char *m; f) int *a[30];

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 17

b) double x,y;
g) float f(int *a, int * b);
double *px, *py;
c) int a=25;
h) int (*pfunc)(void);
int *k=&a;
d) float (*q)[20]; i) char [3]={“rojo”,”gris”,”azul”}
e) float x,y;
j) float(*pfunc)(int *a,char b);
float *px; *py=&y;

2  Usando la sintaxis de C++ escriba el código que crea necesario para:


a) Declarar un puntero a datos de tipo flotante y otro puntero a datos de doble
precisión.
b) Declarar un puntero a una lista de 10 arreglos contiguos de 15 elementos
enteros cada uno. Reserve la memoria necesaria.
c) Declarar un arreglo de punteros para representar una matriz de 10x30 ele-
mentos flotantes.
d) Declarar un puntero a una función que acepte 2 parámetros de doble preci-
sión y no devuelva resultado alguno.
e) Declarar una función que tenga otra función como argumento y devuelva un
puntero a un entero. La función argumento debe tener 2 parámetros enteros
y devuelve un carácter.

3  Observe la porción de código C++ del recuadro .....


de la derecha y determine la salida que se ob- int a=90;
tiene de los flujos de salida cout propuestos. int *p=&a;
Considere que la variable a se ha almacenado int b=(*p)++;
int *q=p+2;
en memoria a partir de la dirección 0000FF09. cout<<p<<" "<<*p;
cout<<q<<" "<<*q;
cout<<a<<" "<<b;
4  Analice el código C++ del recuadro de abajo p++; b=*(q--)-1;
para responder lo siguiente: a=(*p++)+1;
a) Qué tipo de parámetros actuales se emple- cout<<a<<” “<<b;
an para llamara a func ?. .....
b) Qué tipos de parámetros formales se defi-
nen en func?.
c) Qué tipo de información devuelve la función func ?.
d) Cuál es la salida que se obtiene en el programa correspondiente al código
propuesto para func ?.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 18

....
void func(int *p)
{int i, sum=0;
for (i=0;i<6;i++)
sum+=*(p+1);
cout<<"sum="<<sum<<endl;
}

int main()
{int x[6]={12,34,56,78,39,90};
func(x);
.....

5  A continuación se declara un arreglo a de 10 elementos enteros. El elemento


inicial x[0]se ubica en la dirección de memoria 000011E4:
int a[10]={110, 120, 130, 140, 150, 160, 170, 180, 190,
200};
Determine el valor que representan las expresiones siguientes:
a) x;
b) (x+4);
c) *x;
d) *(x+3);
e) *x+3;

6  Utilizando notación de punteros generar aleatoriamente un arreglo lineal A de


120 elementos numéricos, con enteros entre 1000 y 1500 y mostrarlo en pan-
talla.

7  Amplíe el programa anterior para que luego de generar el arreglo aleatorio,


permita ingresar un valor M que debe ser insertado en la posición 32 de dicho
arreglo. Informar el vector modificado.

8  Utilizar la notación de punteros de C++ para leer N datos numéricos y almace-


narlos en un arreglo de flotantes. Obtener la media (M) y la desviación standard
(DS) de la lista. Las expresiones para el cálculo son las siguientes:

x1 + x2 + x 3 +........ + xn
M=
N
( x1 − m ) 2 + ( x 2 − m ) 2 +........ + ( xn − m ) 2
DS =
N

9  Usando notación de punteros genere aleatoriamente una matriz de números


reales de doble precisión de 10 filas por 6 columnas y determine e informe:
a) El promedio de la fila que el usuario ingrese como dato.
b) La suma de cada columna.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 19

10 Escriba una función que utilice punteros para buscar e informar la dirección
de un entero dentro de un arreglo. Se pasan como parámetros el arreglo y el
entero a buscar. Si el dato no se encuentra informar la dirección nula (NULL).

11 Escriba un programa C++ que defina e inicialice un arreglo x de 10 enteros.


El programa debe llamar a una función nuevoarreglo empleando como pa-
rámetro actual de llamada un arreglo p de punteros a los elementos del arre-
glo x.
La función debe retornar un nuevo arreglo con el doble de cada uno de los 10
datos apuntados por los elementos del arreglo p. En el recuadro se propone
la función a emplear donde n es la cantidad de elementos del arreglo.

int *nuevoarreglo(int p[], int n)


{
int *const y=new int[n];
for (int i=0;i<n;i++)
y[i]=*p[i] * 2;
return y;
}
12 Escriba un programa C++ que invoque a una función mayor() que acepte
como parámetro un arreglo de n punteros a enteros y devuelva un puntero al
mayor entero de la lista . En main() muestre el mayor entero de la lista.

13 Escriba la función Plural cuyo prototipo se expone a continuación del. Debe
retornar en el mismo parámetro string el plural correspondiente. Considere
agregar 's' si la palabra termina n vocal y 'es' si termina en consonante.
void plural (char *p);

Cuestionario
1 ¿Qué es un puntero? ¿Cómo se declara en C++ ?

2 Indique 2 formas de emplear el operador de referencia &.

3 ¿Cómo se expresa el contenido de una variable apuntada por un puntero q?

4 ¿Cuál es la diferencia entre las dos acciones siguientes que emplean el ope-
rador &?
char &a=b;
p=&b;

5 Proponga 2 maneras de acceder a la dirección del sexto elemento de un


arreglo x.

FICH - UNL
Programación Orientada a Objetos-2009
Unidad 1 20

6 ¿ Qué operaciones pueden realizarse con punteros ?. Ejemplifique.

7 Si p es un puntero a enteros y q un puntero a datos de tipo double, cuál es la


diferencia en bytes entre: a) p y p+4 b) p y p-1
c) q y q+5 d) q y q++

8 Considere un arreglo x y un índice de tipo entero i. ¿ Cuál es el resultado de


la expresión x[i]==i[x] ?. Si dicha expresión es ilegal explique el motivo.

9 Si una cantidad n es sumada a una variable puntero. Interprete el resultado


de dicho cálculo. ¿Y si se resta n del puntero?.

10 De acuerdo con la siguiente declaración de las variables dato y p:


int dato=25;
int *const p=&dato;
Determine si son válidas las siguientes proposiciones. Justifique en caso de
responder negativamente
a) dato=100; c) p=&(dato+1)
b) p++; d) p=NULL;

11 Explique la diferencia entre las dos declaraciones siguientes:


float func(); float (*func)();

12 ¿Cuál es la ventaja de emplear variables dinámicas ? ¿Cómo las define en


C++?

FICH - UNL
Programación Orientada a Objetos-2009