Escolar Documentos
Profissional Documentos
Cultura Documentos
Computacin Apuntadores
APUNTADORES
1. Introduccin. Un apuntador es la direccin en memoria de una variable. La memoria de una computadora se divide en posiciones de memoria numeradas (llamadas bytes), y las variables se implementan como una sucesin de posiciones de memoria adyacentes. En ocasiones el sistema C++ utiliza estas direcciones de memoria como nombres de las variables. Si una variable se implementa como tres posiciones de memoria, la direccin de la primera de esas posiciones a veces se usa como nombre para esa variable. Por ejemplo, cuando la variable se usa como argumento de llamada por referencia, es esta direccin, no el nombre del identificador de la variable, lo que se pasa a la funcin invocadora. Una direccin que se utiliza para nombrar una variable de este modo (dando la direccin de memoria donde la variable inicia) se llama apuntador porque podemos pensar que la direccin apunta a la variable. La direccin apunta a la variable porque la identifica diciendo dnde est, en lugar de decir qu nombre tiene. Digamos que una variable est en la posicin nmero 1007, podemos referirnos a ella diciendo es la variable que est all, en la posicin 1007. En varias ocasiones hemos empleado apuntadores: cuando una variable es un argumento de llamada por referencia en una llamada de funcin, la funcin recibe esta variable argumento en forma de un apuntador a la variable. stos son dos usos importantes de los apuntadores, pero el sistema C++ se encarga de ello automticamente, aunque tambin proporciona la posibilidad de escribir programas que manipulen apuntadores de cualquier forma que queramos. 2. Definicin y declaracin. Un apuntador, llamado tambin puntero, es aquella variable que contiene una direccin de memoria, normalmente la direccin de otra variable. Se pueden tener apuntadores a cualquier tipo de variable. Un apuntador se puede guardar en una variable. Sin embargo, aunque un apuntador es una direccin de memoria y una direccin de memoria es un nmero, no podemos guardar un apuntador en una variable de tipo int o double. Una variable que va a contener un apuntador se debe declarar como de tipo apuntador. Por ejemplo, lo que sigue declara p como una variable de apuntador que puede contener un apuntador que apunta a una variable de tipo double.
double *p;
La variable p puede contener apuntadores a variables de tipo double, pero normalmente no puede contener un apuntador a una variable de algn otro tipo, como int o char. Cada tipo de variable requiere un tipo de apuntador distinto. En general, si queremos declarar una variable que pueda contener apuntadores a otras variables de un tipo especfico, declaramos variables de apuntador igual que declaramos una variable ordinaria de ese tipo, pero colocamos un asterisco antes del nombre de la variable. Por ejemplo, lo siguiente declara las variables ptr1 y ptr2 de modo que puedan contener apuntadores a variables de tipo int; tambin se declaran dos variables ordinarias var1 y var2 de tipo int:
int *ptr1, *ptr2, var1, var2;
-1-
Computacin Apuntadores
Es necesario que haya un asterisco antes de cada una de las variables de apuntador. Si omitimos el segundo asterisco en la declaracin anterior, ptr2 no ser una variable de apuntador; ser una variable ordinaria de tipo int. Cuando tratamos apuntadores y variables de apuntador, normalmente hablamos de apuntar en lugar de hablar de direcciones. Cuando una variable de apuntador, como ptr1, contiene la direccin de una variable, como var1, decimos que dicha variable apunta a la variable var1 o es un apuntador a la variable var1. 3. Operadores de apuntadores. 3.1. Operador de direccin (&). Permite determinar la direccin de una variable cualquiera (puede ser incluso la direccin de una variable apuntador). Este operador unario se puede anteponer a cualquier variable. 3.2. Operador de indireccin (*). Tambin llamado operador de desreferenciacin. Permite determinar el valor o contenido de lo que se encuentra en la direccin apuntada por la variable direccin (apuntador). Este operador unario se puede anteponer a una variable apuntador o puntero. 4. Inicializacin. Cuando un apuntador es declarado apunta a algn lado. Se debe inicializar el apuntador antes de usarlo.
int *ptr1; int var1; ptr1 = &var1; *ptr1 = 100; // Se // Se // El // Se
declara un apuntador a un entero. declara una variable entera. apuntador recibe la direccin de var1. asigna valor a var1 a travs de su apuntador.
ptr1
var1 100
int
int
Figura 1. Representacin grfica de: ptr1 apunta a la variable var1. Ahora tenemos dos formas de referirnos a var1: podemos llamarla var1 o la variable a la que ptr1 apunta. En C++ la forma de decir la variable a la que ptr1 apunta es *ptr1. ste es el mismo asterisco que usamos al declarar ptr1, pero ahora tiene otro significado. Cuando el asterisco se usa de esta manera se le conoce como operador de desreferenciacin, y decimos que la variable de apuntador est desreferenciada.
-2-
Computacin Apuntadores
En tanto ptr1 contenga un apuntador que apunte a var1, entonces var1 y *ptr1 se referirn a la misma variable. As pues, si asignamos 100 a *ptr1, tambin estamos asignando 100 a var1. El smbolo & que usamos para obtener la direccin de una variable es el mismo smbolo que usamos en una declaracin de funcin para especificar un parmetro de llamada por referencia. Esto no es una coincidencia. Recuerde que un argumento de llamada por referencia se implementa dando la direccin del argumento a la funcin invocadora. As pues, estos dos usos del smbolo & son bsicamente el mismo. 5. Apuntadores en instrucciones de asignacin. Podemos asignar el valor de una variable de apuntador a otra variable de apuntador. Esto copia una direccin de una variable de apuntador a otra. Por ejemplo, si ptr1 todava est apuntando a var1, lo siguiente establecer el valor de ptr2 de modo que tambin apunte a var1:
ptr2 = ptr1;
Siempre que no hayamos modificado el valor de var1, lo siguiente tambin desplegar 100 en la pantalla:
cout << *ptr2 << endl; // Imprimir el nmero 100.
Asegrese de no confundir:
ptr1 = ptr2;
con
*ptr1 = *ptr2;
Cuando aadimos el asterisco, no estamos tratando con los de apuntadores prt1 y ptr2, sino con las variables a las que estos apuntan. Esto se ilustra en las figuras siguientes.
-3-
Computacin Apuntadores
Figura 3. Representacin grfica de: *ptr1 = *ptr2. 6. Apuntadores y funciones. Cuando se pasan argumentos a funciones, estos son pasados por valor, si el parmetro es modificado dentro de la funcin, una vez que termina la funcin el valor pasado de la variable permanece inalterado. Hay muchos casos que se requiere alterar el argumento pasado a la funcin y recibir el nuevo valor una vez que la funcin ha terminado. Para hacer lo anterior se debe usar una llamada por referencia, lo cual se puede simular pasando un puntero al argumento. Con esto se provoca que la computadora pase la direccin del argumento a la funcin. Ejemplo 1. Escriba un programa que intercambie el valor de dos variables. El intercambio debe realizarse dentro de una funcin a la cual se hace una llamada por referencia.
#include #include #include #include #include "stdafx.h" "iostream" "iomanip" <conio.h> <windows.h>
using namespace std; void leeValor(int &, char *); void intercambio(int *, int *); void imprime(int *, int *);
-4-
Computacin Apuntadores
7.
Apuntadores y arreglos. El nombre de un arreglo sin ndice representa la direccin del primer elemento del arreglo: Si se tiene: Entonces:
char p[10]; p representa &p[0];
Una variable arreglo puede ser utilizada como si fuera un apuntador, y cualquier apuntador inicializado con la direccin de un elemento de un arreglo (generalmente el primero) puede utilizarse como si estuviera declarado como arreglo:
int *ptr1, a[10]; ptr1 = a;
Entonces:
a[5] = 100;
-5-
Computacin Apuntadores
Ejemplo 2. Escriba un programa que permita ingresar nmeros enteros en un arreglo, los ordene ascendentemente y luego los reporte.
#include #include #include #include #include "stdafx.h" "iostream" "iomanip" <conio.h> <windows.h>
using namespace std; void ingreso(int [], int); void ordena(int [], int); void reporte(int [], int); const int TAM=20; int _tmain(int argc, _TCHAR* argv[]) { int a[TAM], n; cout<<endl<<setw(40)<<"Puntero a arreglo de enteros"<<endl; do { cout<<endl<<"Numero de elementos (1-"<<TAM<<"): "; cin>>n; }while(n<1 || n>TAM); system("cls"); cout<<endl<<setw(40)<<"Ingreso de datos al arreglo"<<endl; ingreso(a, n); cout<<endl<<"Termino el ingreso de datos."<<endl <<"Presione una tecla para continuar..."; _getch(); system("cls"); ordena(a, n); cout<<endl<<setw(40)<<"Listado ordenado ascendentemente"<<endl; reporte(a, n); _getch(); return 0; } void ingreso(int *pA, int n) { cout<<endl; for(int i=0; i<n; i++) { cout<<"Elemento "<<(i+1)<<": "; cin>>*pA; ++pA; } } void ordena(int *pA, int n) { int *pB, temp; pB = pA; for(int i=0; i<n-1; i++) { for(int j=i; j<n; j++) { if(*pA > *pB) { temp = *pA;
-6-
Computacin Apuntadores
En este ejemplo la funcin ordenAscendente(int *ptrA, int n) tambin pudo haberse escrito de la siguiente manera:
void ordenAscendente(int *ptrA, int n) { int *ptrAux, aux; ptrAux = ptrA; for(int i=0; i<n-1; i++) { for(int j=i+1; j<n; j++) { if(ptrA[i] > ptrAux[j]) { aux = ptrA[i]; ptrA[i] = ptrAux[j]; ptrAux[j] = aux; } } } }
8.
Apuntadores y matrices. Si declaramos una matriz int M[10][10], M[i] representa &M[i][0], siendo i un entero de 0 a 9. Cuando se pasa un arreglo bidimensional a una funcin se debe especificar el nmero de columnas el nmero de filas es irrelevante. La razn de lo anterior, es nuevamente los apuntadores. El compilador requiere conocer cuntas son las columnas para que pueda saltar de fila en fila en la memoria. Considerando que una funcin deba recibir int M[5][10], se puede declarar el argumento de la funcin como:
funcionX(int M[][10]) { }
o an:
funcionX(int (*M)[10]) { }
En la ltima sentencia se requieren los parntesis (*M) ya que los corchetes tienen una precedencia ms alta que el asterisco. Ejemplo 3. Escriba un programa que lea lneas de texto hasta encontrar una lnea en blanco, entonces vuelve a mostrar cada lnea de dos maneras: caracter a caracter e indexando slo la primera dimensin y utilizando el formato cadena.
#include "stdafx.h" #include "iostream" #include "iomanip"
-7-
Computacin Apuntadores
int _tmain(int argc, _TCHAR* argv[]) { char texto[MAX][longitud]; int i; cout<<endl<<setw(40)<<"Ingreso de lineas de texto"<<endl; cout<<endl<<"Ingrese una linea en blanco para terminar..." <<endl<<endl; ingreso(texto,i); system("cls"); cout<<endl<<setw(40)<<"Texto mostrado caracter a caracter" <<endl<<endl; mostrarCaracteres(texto, i); cout<<endl<<endl<<setw(48)<<"Texto mostrado con el formato de cadena" <<endl<<endl; mostrarLineas(texto, i); _getch(); return 0; } void ingreso(char (*ptrT)[longitud], int &i) { cout.setf(ios::left); for(i=0; i<MAX; i++) { cout<<setw(6)<<"Linea"<<setw(2)<<(i+1)<<setw(3)<<":"; cin.getline(ptrT[i], 80); if(!*ptrT[i]) // Equivale a *&texto[t][0] break; } cout<<resetiosflags(ios::left); } void mostrarCaracteres(char (*ptrT)[longitud], int i) { cout.setf(ios::left); for(int t=0; t<i; t++) { cout<<setw(6)<<"Linea"<<setw(2)<<(t+1)<<setw(3)<<":"; for(int j=0; ptrT[t][j]; j++) cout<<setw(2)<<ptrT[t][j]; cout<<endl; } cout<<resetiosflags(ios::left); } void mostrarLineas(char (*ptrT)[longitud], int i) { cout.setf(ios::left); for(int t=0; t<i; t++) cout<<setw(6)<<"Linea"<<setw(2)<<(t+1)<<setw(3)<<": "<<ptrT[t] <<endl; cout<<resetiosflags(ios::left); }
-8-
Computacin Apuntadores
Ejemplo 4. Escriba un programa que permita, haciendo uso de apuntadores, ingresar nmeros en una matriz y luego reporte los datos ingresados.
#include #include #include #include "stdafx.h" "iostream" "iomanip" <conio.h>
using namespace std; const int TAM = 10; void dimension(int &, char *); void ingreso(int (*)[TAM], int, int); void reporte(int (*)[TAM], int, int); int _tmain(int argc, _TCHAR* argv[]) { int a[TAM][TAM], fil, col; cout<<endl<<setw(40)<<"Puntero a Matriz de numeros"<<endl<<endl; cout.setf(ios::left); dimension(fil, "Numero de filas: "); dimension(col, "Numero de columnas: "); cout<<resetiosflags(ios::left); system("cls"); cout<<endl<<setw(30)<<"Ingreso de datos a la matriz"<<endl<<endl; ingreso(a, fil, col); cout<<endl<<endl<<"Termino ingreso de datos" <<endl<<"Presione una tecla para continuar..."<<endl; _getch(); system("cls"); cout<<endl<<setw(30)<<"Reporte de datos de la matriz"<<endl<<endl; reporte(a, fil, col); _getch(); return 0; } void dimension(int &dim, char *cad) { do { cout<<setw(20)<<cad; cin >> dim; }while(dim <= 0 || dim >= TAM); } void ingreso(int (*ptrA)[TAM], int fil, int col) { for(int i=0; i<fil; i++) { for(int j=0; j<col; j++) { cout<<setw(7)<<"M[" << i << "][" << j << "]: "; cin>>(*ptrA)[j]; } ++ptrA; } } void reporte(int (*ptrA)[TAM], int fil, int col) { for(int i=0; i<fil; i++) { for(int j=0; j<col; j++) cout<<setw(7)<<ptrA[i][j]; cout<<endl; } }
-9-
Computacin Apuntadores
Para trabajar sobre un arreglo de estructuras se aplican los mismos mecanismos que para trabajar con un arreglo de nmeros enteros por ejemplo, haciendo la salvedad que en la estructura no se asigna un nico valor sino tantos valores como miembros se hayan definido en ella. Ejemplo 5. Escriba un programa que permita, haciendo uso de estructuras y apuntadores, ingresar el nombre y la edad de N personas, y que luego reporte dichos datos ordenados ascendentemente por el nombre.
#include #include #include #include #include #include "stdafx.h" "iostream" "iomanip" <string.h> <conio.h> <windows.h>
using namespace std; struct datos { char nombre[30]; int edad; }; const int TAM = 20; void void void void numeroPersonas(int &); ingreso(datos [], int); ordenaPorNombre(datos [], int); reporte(datos [], int);
int _tmain(int argc, _TCHAR* argv[]) { datos d[TAM]; int n; cout<<endl<<setw(50)<<"Arreglo de estructuras"<<endl<<endl; numeroPersonas(n); system("cls"); cout<<endl<<endl<<setw(30)<<"Ingreso de datos"<<endl<<endl; ingreso(d, n); cout<<endl<<endl<<"Termino el ingreso de datos." <<endl<<"Presione una tecla para continuar..."; _getch(); system("cls"); cout<<endl<<endl<<setw(30)<<"Listado original"<<endl<<endl; reporte(d, n); cout<<endl<<endl<<setw(35)<<"Listado ordenado por nombre" <<endl<<endl; ordenaPorNombre(d,n); reporte(d,n); _getch(); return 0; } void numeroPersonas(int &n) { do { cout<<"Numero de personas (1-"<<TAM<<"): "; cin>>n; }while(n<1 || n>TAM); cin.get(); }
- 10 -
Computacin Apuntadores
void reporte(datos *pD, int n) { cout.setf(ios::left); cout<<setw(5)<<""<<setw(30)<<"Nombre"<<setw(5)<<"Edad"<<endl; for(int i=0; i<n; i++) cout<<setw(5)<<""<<setw(30)<<pD[i].nombre<<setw(5)<<pD[i].edad <<endl; cout<<resetiosflags(ios::left); }
10. Arreglos de apuntadores. Los apuntadores pueden estructurarse en arreglos como cualquier otro tipo de dato. La declaracin para un arreglo de punteros a enteros de tamao 10 es:
int *a[10];
Para asignar la direccin de una variable entera llamada num al tercer elemento del arreglo de punteros, se debe indicar:
a[2] = #
- 11 -
Computacin Apuntadores
Slo se puede realizar suma y resta de apuntadores. Cada vez que la variable apuntador se incrementa, apunta a la direccin de memoria del siguiente elemento de su tipo base. Cada vez que la variable apuntador disminuye, apunta a la posicin del elemento anterior. Tambin se les puede sumar y restar enteros a los apuntadores, considerando siempre que lo que se suma y se resta son posiciones de tipo base:
int *ptr1; ptr1 = ptr1 + 9;
Dado que la variable ptr1 es de tipo apuntador a entero de 4 bytes, luego de la suma, apuntar 36 (9 x 4) bytes mas all del valor inicial. La razn por la cual se asocia un apuntador a un tipo de dato, es por que se debe conocer en cuantos bytes est guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador por un bloque de memoria, en donde el bloque est en funcin del tamao del dato. Por lo tanto para un apuntador a un char, se agrega un byte a la direccin y para un apuntador a int o a float se agregan 4 bytes. De esta forma si a un apuntador a float se le suma 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes. 12. Apuntadores a apuntadores. Denominado tambin indireccin mltiple o encadenamiento de punteros.
int *ptr1; int **ptr2; // Se declara un apuntador a un entero. // Se declara apuntador a otro apuntador.
- 12 -