25
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

POO Punteros en C++

Embed Size (px)

DESCRIPTION

Apunte teorico de PUNTEROS en C++

Citation preview

Page 1: POO Punteros en C++

Universidad Nacional del LitoralFacultad 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

Page 2: POO Punteros en C++

Unidad 1 2

Unidad 1Punteros

Resumen de Conceptos

IntroducciónUn 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 elemento. 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 ejecución.

Muchas funciones predefinidas de C++ emplean punteros como argumentos e inclusive devuelven punteros. Para operar con punteros C++ dispone de los operadores & 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 PunterosPara 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

*x

FICH - UNLProgramación Orientada a Objetos-2009

dirección de la variable dato

Dato apuntado por x

Page 3: POO Punteros en C++

Unidad 1 3

En el ejemplo anterior, la variable puntero x contiene la dirección donde se almacenará un dato de tipo entero, pero *x es una expresión nemotécnica que corresponde 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 almacenado 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 través del operador * y asignarle la dirección de memoria de algún elemento del programa.

Pero si declaramos una variable simple cualquiera: ¿Cómo determinamos su direcció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 zint *p; // declaramos un puntero p a un enterop= &z; // asignamos la dirección de z al puntero pcout<< *p <<endl; // informamos 8, el dato apuntado por pcout<< 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 declarada 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 operadores:

&: operador dirección o referencia*: operador indirección o desreferencia;

La constante NULLSi 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 - UNLProgramación Orientada a Objetos-2009

Variable de tipo puntero

x

Page 4: POO Punteros en C++

Unidad 1 4

Nota: la mayoría de los compiladores C++ devuelven un entero en base hexadecimal de 8 cifras para definir una dirección de memoria.

FICH - UNLProgramación Orientada a Objetos-2009

Page 5: POO Punteros en C++

Unidad 1 5

Variables dinámicas: new y deleteAl 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.14159float *q=&x ; // q contiene la dirección de xcout<<*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 qq= 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 tiem-po de ejecución se puede efectuar la operación delete inversa a new, desalo-cando 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 PunterosLas variables puntero pueden ser operadas aritméticamente; esto nos permite referenciar 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 bytes) */double *r;

FICH - UNLProgramación Orientada a Objetos-2009

Page 6: POO Punteros en C++

Unidad 1 6

r+=2; /*La nueva dirección de r supera a la anterior en 16 bytes pues al incrementar en 2 su dirección, la estamos desplazando la cantidad de bytes correspondientes 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 puntero.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 direcció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 floatq= 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ónEl objeto de pasar punteros a una función es poder modificar los parámetros de llamada, es decir, permite efectuar un pasaje por referencia.

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

FICH - UNLProgramación Orientada a Objetos-2009

Page 7: POO Punteros en C++

Unidad 1 7

Esto es radicalmente diferente al pasaje de parámetros por valor donde las direcciones de los argumentos de llamada son diferentes a las direcciones de los argumentos 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);

void main( ){ 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 través de punteros:

/*** ejemplo ***/#include <iostream.h>#include <conio.h>

void f_porpunteros(int *p,int *q);

void main( ){ 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 - UNLProgramación Orientada a Objetos-2009

Llamada a la función f_porpunteros donde los parámetros actuales son pasados por referencia.

Llamada a la función f_porvalor donde los parámetros actuales son pasados por valor.

Page 8: POO Punteros en C++

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 considerar 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 linealesEn C++ los punteros y el tipo arreglo están íntimamente relacionados. Las declaraciones de arreglos que hemos estudiado pueden plantearse a través de punteros logrando 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 - UNLProgramación Orientada a Objetos-2009

Page 9: POO Punteros en C++

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:

O012FF78O012FF7CO012FF80O012FF84O012FF88

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 arreglo 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 ocupa 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 elemento 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 manera 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. posible 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 almacenar 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 - UNLProgramación Orientada a Objetos-2009

Page 10: POO Punteros en C++

Unidad 1 10

/* Ejemplo: generar aleatoriamente un arreglo de 20 números natura-les menores que 1000. Luego insertar en la posición 5 (sexto elemen-to) el valor -45. El programa debe mostrar el nuevo arreglo de 21 elementos */#include <stdlib.h>#include <alloc.h>#include <iostream>#include <iomanip.h>using namespace std;

void main(){int *x,i;x=(int*) malloc( 21*sizeof(int) );

for(i=0; i<20; i++) { *(x+i)=rand()%(1000); cout<<setw(8)<< *(x+i); }

for (i=20; i>5; i--) *(x+i)=*(x+i-1);

*(x+5)=-45;

cout<<endl<<endl;for( i=0; i<21; i++) cout<<setw(8)<< *(x+i);

} // fin del programa

Punteros a Arreglos MultidimensionalesConsideremos 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 elemento, una lista de arreglos unidimensionales para plantear una matriz puede definirse 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]

FICH - UNLProgramación Orientada a Objetos-2009

Reserva de memoria para 21 elementos enteros del arreglo x

En el for se genera y muestra el arreglo de enteros aleatorios

Se desplazan los elementos para poder usar la posición 5 e insertar el nuevo dato.

Se inserta el dato -45 en la posición 5 del array.

Se muestra el nuevo arreglo de 21 componentes.

Page 11: POO Punteros en C++

Primer arreglo lineal

Unidad 1 11

Es muy importante la presencia del paréntesis (*z), pues de otro 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 elemento del primer arreglo.

Tomemos el caso de la fila 4 (quinta fila)

z+4 23 27 39 21 20 28 100

*(z+4) *(z+4)+6

Dirección del elemento [4,0]

Dirección del 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

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 puntero 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)

FICH - UNLProgramación Orientada a Objetos-2009

z 56 78 65 64 90 81 77

z+1 11 10 15 18 16 12 14

z+2 43 40 41 67 44 45 62

z+3 101 190 87 105 123 114 79

z+4 23 27 39 21 20 28 100

z+5 66 68 33 72 31 60 99

Segundo arreglo lineal

Sexto arreglo lineal

Puntero al inicio del 3er arreglo

Puntero al inicio de la quinta fila (5to arreglo)

Puntero al inicio del 5to arreglo

Puntero al inicio de la quinta fila (5to arreglo)

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

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

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

Page 12: POO Punteros en C++

Unidad 1 12

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

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 constEmpleando 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 constanteint dato=25;int *const p=&dato;

Para definir una constante apuntado por un puntero qfloat 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

FICH - UNLProgramación Orientada a Objetos-2009

Page 13: POO Punteros en C++

Unidad 1 13

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 punteros. 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 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

p[0] 56 78 65 64 90 81 77

p[1] 11 10 15 18 16 12 14

p[2] 43 40 41 67 44 45 62

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

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 puntero (p[4]+5) estamos haciendo referencia a la dirección (puntero) del sexto elemento 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

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

FICH - UNLProgramación Orientada a Objetos-2009

Primer arreglo lineal

Segundo arreglo lineal

3er elemento del arreglo de punteros

Sexto arreglo lineal

Primer puntero del arreglo

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

Page 14: POO Punteros en C++

Unidad 1 14

#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++) 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 FuncionesComo 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 estructuras.

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 interpretarse 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 dicha 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>

FICH - UNLProgramación Orientada a Objetos-2009

Page 15: POO Punteros en C++

Unidad 1 15

#include <stdio.h>#include <math.h>#include<conio.h>

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)

p(); else

{ 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 llamada. Veamos esto en un ejemplo.

La función sum tiene 2 parámetros: el primero es un valor entero que será acumulado tantas veces como indique el siguiente parámetro. El primer argumento lo representaremos mediante una función que devuelve un entero y acepta como argumento 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;}

FICH - UNLProgramación Orientada a Objetos-2009

Puntero a la función argumento

Llamada a la función argumento (complejas)

Page 16: POO Punteros en C++

Unidad 1 16

En conclusión podemos afirmar que un puntero a una función es un puntero que representa la dirección del nombre de la función- Pero como el nombre de una funció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 fint (*pf)(int x);//declaración del puntero pf a funciónpf=&f; //asignamos la dirección de f al puntero pf

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

FICH - UNLProgramación Orientada a Objetos-2009

int f(int n){ .....

pf

f

Page 17: POO Punteros en C++

Unidad 1 17

Actividades

Ejercicios

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

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

b) double x,y; double *px, *py;

g) float f(int *a, int * b);

c) int a=25; int *k=&a;

h) int (*pfunc)(void);

d) float (*q)[20]; i) char [3]={“rojo”,”gris”,”azul”}

e) float x,y; float *px; *py=&y;

j) float(*pfunc)(int *a,char b);

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

elementos flotantes.d) Declarar un puntero a una función que acepte 2 parámetros de doble

precisió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 obtiene de los flujos de salida cout propuestos.Considere que la variable a se ha almacenado en memoria a partir de la dirección 0000FF09.

4 Analice el código C++ del recuadro de abajo para responder lo siguiente:a) Qué tipo de parámetros actuales se

emplean para llamara a func ?.b) Qué tipos de parámetros formales se

definen 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 - UNLProgramación Orientada a Objetos-2009

.....int a=90; int *p=&a;int b=(*p)++; int *q=p+2;cout<<p<<" "<<*p;cout<<q<<" "<<*q;cout<<a<<" "<<b;p++; b=*(q--)-1;a=(*p++)+1;cout<<a<<” “<<b;.....

Page 18: POO Punteros en C++

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 pantalla.

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 almacenarlos 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:

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 - UNLProgramación Orientada a Objetos-2009

Page 19: POO Punteros en C++

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

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 parámetro actual de llamada un arreglo p de punteros a los elementos del arreglo 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.

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 operador &? char &a=b;p=&b;

5 Proponga 2 maneras de acceder a la dirección del sexto elemento de un arreglo x.

FICH - UNLProgramación Orientada a Objetos-2009

Page 20: POO Punteros en C++

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 - UNLProgramación Orientada a Objetos-2009