You are on page 1of 12

Universidad Nacional de Trujillo Ingeniería de Sistemas

Computación Apuntadores

APUNTADORES
1. Introducción. Un apuntador es la dirección 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 sucesión 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 dirección 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 dirección, no el nombre del identificador de la variable, lo que se pasa a la función invocadora. Una dirección que se utiliza para nombrar una variable de este modo (dando la dirección de memoria donde la variable inicia) se llama apuntador porque podemos pensar que la dirección “apunta” a la variable. La dirección “apunta” a la variable porque la identifica diciendo dónde está, en lugar de decir qué nombre tiene. Digamos que una variable está en la posición número 1007, podemos referirnos a ella diciendo “es la variable que está allá, en la posición 1007”. En varias ocasiones hemos empleado apuntadores: cuando una variable es un argumento de llamada por referencia en una llamada de función, la función 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 automáticamente, aunque también proporciona la posibilidad de escribir programas que manipulen apuntadores de cualquier forma que queramos. 2. Definición y declaración. Un apuntador, llamado también puntero, es aquella variable que contiene una dirección de memoria, normalmente la dirección 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 dirección de memoria y una dirección de memoria es un número, 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 algún 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 específico, 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; también se declaran dos variables ordinarias var1 y var2 de tipo int:
int *ptr1, *ptr2, var1, var2;

Ing. Zoraida Yanet Vidal Melgarejo, Mg.

-1-

3. -2- . 3. Operadores de apuntadores. declara una variable entera. Operador de dirección (&). Se debe inicializar el apuntador antes de usarlo. Cuando una variable de apuntador.1. Permite determinar el valor o contenido de lo que se encuentra en la dirección apuntada por la variable dirección (apuntador). ptr1 = &var1. normalmente hablamos de apuntar en lugar de hablar de direcciones. Éste es el mismo asterisco que usamos al declarar ptr1. Cuando tratamos apuntadores y variables de apuntador. Permite determinar la dirección de una variable cualquiera (puede ser incluso la dirección de una variable apuntador). y decimos que la variable de apuntador está desreferenciada. int *ptr1. como ptr1. Representación gráfica de: ptr1 apunta a la variable var1. *ptr1 = 100. Ahora tenemos dos formas de referirnos a var1: podemos llamarla var1 o “la variable a la que ptr1 apunta”. int var1. Operador de indirección (*). Si omitimos el segundo asterisco en la declaración anterior. pero ahora tiene otro significado. apuntador recibe la dirección de var1.Universidad Nacional de Trujillo Ingeniería de Sistemas Computación Apuntadores Es necesario que haya un asterisco antes de cada una de las variables de apuntador. contiene la dirección de una variable. Ing. Este operador unario se puede anteponer a una variable apuntador o puntero. 3. Zoraida Yanet Vidal Melgarejo. como var1. será una variable ordinaria de tipo int. Inicialización. Podemos representar gráficamente que ptr1 apunta a la variable var1: ptr1 var1 100 int int Figura 1. Mg. Cuando un apuntador es declarado apunta a algún lado. // Se // Se // El // Se declara un apuntador a un entero. asigna valor a var1 a través de su apuntador. Este operador unario se puede anteponer a cualquier variable. En C++ la forma de decir “la variable a la que ptr1 apunta” es *ptr1. decimos que dicha variable apunta a la variable var1 o es un apuntador a la variable var1. ptr2 no será una variable de apuntador. También llamado operador de desreferenciación. 4.2. Cuando el asterisco se usa de esta manera se le conoce como operador de desreferenciación.

En tanto ptr1 contenga un apuntador que apunte a var1. entonces var1 y *ptr1 se referirán a la misma variable. si asignamos 100 a *ptr1. Mg. Recuerde que un argumento de llamada por referencia se implementa dando la dirección del argumento a la función invocadora. Esto no es una coincidencia. El símbolo & que usamos para obtener la dirección de una variable es el mismo símbolo que usamos en una declaración de función para especificar un parámetro de llamada por referencia. Podemos asignar el valor de una variable de apuntador a otra variable de apuntador. sino con las variables a las que estos apuntan.Universidad Nacional de Trujillo Ingeniería de Sistemas Computación Apuntadores Si ahora ejecutamos las siguientes sentencias. Asegúrese de no confundir: ptr1 = ptr2. Esto copia una dirección de una variable de apuntador a otra. no estamos tratando con los de apuntadores prt1 y ptr2. // Imprimirá el número 100. Así pues. con *ptr1 = *ptr2. Esto se ilustra en las figuras siguientes. Por ejemplo. // Imprimirá el número 100. // Imprimirá el número 100. estos dos usos del símbolo & son básicamente el mismo. Siempre que no hayamos modificado el valor de var1. Así pues. Apuntadores en instrucciones de asignación. si ptr1 todavía está apuntando a var1. cout << *ptr1 << endl. Cuando añadimos el asterisco. Zoraida Yanet Vidal Melgarejo. desplegarán el mismo valor en la pantalla: cout << var1 << endl. lo siguiente establecerá el valor de ptr2 de modo que también apunte a var1: ptr2 = ptr1. también estamos asignando 100 a var1. 5. Ing. lo siguiente también desplegará 100 en la pantalla: cout << *ptr2 << endl. -3- .

void leeValor(int &.h> <windows. Cuando se pasan argumentos a funciones. si el parámetro es modificado dentro de la función. char *). Ejemplo 1. lo cual se puede simular pasando un puntero al argumento. void imprime(int *. una vez que termina la función el valor pasado de la variable permanece inalterado. int *). int *). Mg. El intercambio debe realizarse dentro de una función a la cual se hace una llamada por referencia.h> using namespace std. Con esto se provoca que la computadora pase la dirección del argumento a la función.h" "iostream" "iomanip" <conio. void intercambio(int *. estos son pasados por valor. Para hacer lo anterior se debe usar una llamada por referencia. -4- . Zoraida Yanet Vidal Melgarejo. Representación gráfica de: *ptr1 = *ptr2. Ing. 6. Representación gráfica de: ptr1 = ptr2. #include #include #include #include #include "stdafx. Apuntadores y funciones. Antes: ptr1 var1 84 ptr2 var2 99 *ptr1 = *ptr2 ptr2 ptr1 Después: var1 99 var2 99 Figura 3.Universidad Nacional de Trujillo Ingeniería de Sistemas Computación Apuntadores Antes: ptr1 var1 84 ptr2 var2 99 ptr1 = ptr2 ptr2 ptr1 Después: var1 84 var2 99 Figura 2. Hay muchos casos que se requiere alterar el argumento pasado a la función y recibir el nuevo valor una vez que la función ha terminado. Escriba un programa que intercambie el valor de dos variables.

} void leeValor(int &n. int *pY) { cout<<endl<<setw(10)<<"X: "<<setw(5)<<*pX<<endl. intercambio(&x. cout<<endl<<setw(30)<<"Intercambio de valores"<<endl. cout<<endl<<endl<<"Presione una tecla para continuar. cin>>n. Zoraida Yanet Vidal Melgarejo. } Computación Apuntadores 7. y. -5- . cout<<endl<<endl<<setw(30)<<"Despues del intercambio"<<endl. a[10]. cout<<endl<<setw(10)<<"Y: "<<setw(5)<<*pY<<endl. } void imprime(int *pX. leeValor(y. Mg. return 0. *pY = temp. &y). p representa &p[0]. "Valor de X: ").Universidad Nacional de Trujillo Ingeniería de Sistemas int _tmain(int argc. char *cad) { cout<<endl<<setw(15)<<cad. &y). Entonces: a[5] = 100. &y). int *pY) { int temp. temp = *pX. "Valor de Y: ").. _getch(). _getch(). } void intercambio(int *pX. Una variable arreglo puede ser utilizada como si fuera un apuntador. imprime(&x. imprime(&x. Apuntadores y arreglos. Ing.". ptr1 = a. leeValor(x. *pX = *pY. _TCHAR* argv[]) { int x. El nombre de un arreglo sin índice representa la dirección del primer elemento del arreglo: Si se tiene: Entonces: char p[10]. y cualquier apuntador inicializado con la dirección de un elemento de un arreglo (generalmente el primero) puede utilizarse como si estuviera declarado como arreglo: int *ptr1.. cout<<endl<<endl<<setw(30)<<" Antes del intercambio "<<endl. system("cls").

Ing. }while(n<1 || n>TAM). cout<<endl<<"Termino el ingreso de datos. void reporte(int []. temp. n.h" "iostream" "iomanip" <conio. int n) { int *pB. _getch(). int). ingreso(a. Mg. i<n-1. ordena(a. Escriba un programa que permita ingresar números enteros en un arreglo. n). return 0. } } void ordena(int *pA. cout<<endl<<setw(40)<<"Listado ordenado ascendentemente"<<endl. _getch(). system("cls"). for(int i=0. j<n. Computación Apuntadores Ejemplo 2. _TCHAR* argv[]) { int a[TAM]. void ordena(int [].. int). i<n. Zoraida Yanet Vidal Melgarejo. i++) { for(int j=i.h> using namespace std. const int TAM=20. system("cls"). for(int i=0. pB = pA. cout<<endl<<setw(40)<<"Ingreso de datos al arreglo"<<endl. cin>>n. -6- . cout<<endl<<setw(40)<<"Puntero a arreglo de enteros"<<endl.. #include #include #include #include #include "stdafx."<<endl <<"Presione una tecla para continuar. j++) { if(*pA > *pB) { temp = *pA. void ingreso(int []. int). n).Universidad Nacional de Trujillo Ingeniería de Sistemas Es lo mismo que: *(a + 5) = 100.". los ordene ascendentemente y luego los reporte. do { cout<<endl<<"Numero de elementos (1-"<<TAM<<"): ".h> <windows. reporte(a. int n) { cout<<endl. int _tmain(int argc. cin>>*pA. i++) { cout<<"Elemento "<<(i+1)<<": ". } void ingreso(int *pA. ++pA. n).

i<n. Considerando que una función deba recibir int M[5][10]. M[i] representa &M[i][0]. La razón de lo anterior. entonces vuelve a mostrar cada línea de dos maneras: caracter a caracter e indexando sólo la primera dimensión y utilizando el formato cadena. pB = pA. i<n-1. ptrAux = ptrA. int n) { int *ptrAux. El compilador requiere conocer cuántas son las columnas para que pueda saltar de fila en fila en la memoria.Universidad Nacional de Trujillo Ingeniería de Sistemas *pA = *pB. ptrA[i] = ptrAux[j]. Si declaramos una matriz int M[10][10]. j<n. for(int i=0. } ++pB. #include "stdafx. Cuando se pasa un arreglo bidimensional a una función se debe especificar el número de columnas – el número de filas es irrelevante. } Computación Apuntadores En este ejemplo la función ordenAscendente(int *ptrA. *pB = temp. -7- . i++) { for(int j=i+1. aux. se puede declarar el argumento de la función como: funcionX(int M[][10]) { … } o aún: funcionX(int (*M)[10]) { … } En la última sentencia se requieren los paréntesis (*M) ya que los corchetes tienen una precedencia más alta que el asterisco. Zoraida Yanet Vidal Melgarejo. int n) { cout<<endl<<endl. for(int i=0. i++) cout<<setw(10)<<"A["<<i<<"]: "<<setw(5)<<pA[i]<<endl. ptrAux[j] = aux. Ejemplo 3. es nuevamente los apuntadores. } } void reporte(int *pA. } ++pA. Escriba un programa que lea líneas de texto hasta encontrar una línea en blanco. j++) { if(ptrA[i] > ptrAux[j]) { aux = ptrA[i]. siendo i un entero de 0 a 9.h" #include "iostream" #include "iomanip" Ing. Mg. } } } } 8. Apuntadores y matrices. int n) también pudo haberse escrito de la siguiente manera: void ordenAscendente(int *ptrA.

} void mostrarCaracteres(char (*ptrT)[longitud].h> using namespace std. cout<<endl<<endl<<setw(48)<<"Texto mostrado con el formato de cadena" <<endl<<endl. void ingreso(char (*)[longitud]. t++) { cout<<setw(6)<<"Linea"<<setw(2)<<(t+1)<<setw(3)<<":". const int MAX = 100. Mg. } cout<<resetiosflags(ios::left). i). int).setf(ios::left). t<i.setf(ios::left).getline(ptrT[i]. const int longitud = 80. for(int j=0. Computación Apuntadores int _tmain(int argc. i<MAX.. } cout<<resetiosflags(ios::left). } Ing. cout<<endl. -8- . } void mostrarLineas(char (*ptrT)[longitud]. i). _TCHAR* argv[]) { char texto[MAX][longitud]." <<endl<<endl.i). mostrarLineas(texto. cout<<endl<<setw(40)<<"Texto mostrado caracter a caracter" <<endl<<endl. int i) { cout. i++) { cout<<setw(6)<<"Linea"<<setw(2)<<(i+1)<<setw(3)<<":". int &i) { cout. system("cls"). t<i. t++) cout<<setw(6)<<"Linea"<<setw(2)<<(t+1)<<setw(3)<<": "<<ptrT[t] <<endl.h> #include <windows. for(i=0. cout<<resetiosflags(ios::left). ptrT[t][j]. for(int t=0. for(int t=0.Universidad Nacional de Trujillo Ingeniería de Sistemas #include <conio. cout<<endl<<"Ingrese una linea en blanco para terminar. cin. j++) cout<<setw(2)<<ptrT[t][j]. void mostrarCaracteres(char (*)[longitud].. int).setf(ios::left). int i) { cout. void mostrarLineas(char (*)[longitud]. cout<<endl<<setw(40)<<"Ingreso de lineas de texto"<<endl. ingreso(texto. _getch(). int i. } void ingreso(char (*ptrT)[longitud]. return 0. 80). mostrarCaracteres(texto. int &). Zoraida Yanet Vidal Melgarejo. if(!*ptrT[i]) // Equivale a *&texto[t][0] break.

void reporte(int (*)[TAM]. j<col. cout<<endl<<setw(30)<<"Ingreso de datos a la matriz"<<endl<<endl. } void dimension(int &dim.h> using namespace std. int). cout<<endl<<setw(40)<<"Puntero a Matriz de numeros"<<endl<<endl.. _getch(). i<fil. } ++ptrA. int fil. } void ingreso(int (*ptrA)[TAM]. void ingreso(int (*)[TAM]. ingresar números en una matriz y luego reporte los datos ingresados. cout<<resetiosflags(ios::left). reporte(a. system("cls"). int. haciendo uso de apuntadores. cin >> dim. int col) { for(int i=0. Mg. }while(dim <= 0 || dim >= TAM). } } void reporte(int (*ptrA)[TAM]. _getch(). char *). dimension(fil. ingreso(a. j++) cout<<setw(7)<<ptrA[i][j].."<<endl. fil. int. "Numero de filas: "). col). i++) { for(int j=0. col). char *cad) { do { cout<<setw(20)<<cad. dimension(col. i<fil. i++) { for(int j=0. fil. j<col. col. return 0.setf(ios::left). int). system("cls"). cout<<endl<<setw(30)<<"Reporte de datos de la matriz"<<endl<<endl. j++) { cout<<setw(7)<<"M[" << i << "][" << j << "]: ". cout<<endl<<endl<<"Termino ingreso de datos" <<endl<<"Presione una tecla para continuar. "Numero de columnas: "). Zoraida Yanet Vidal Melgarejo. cin>>(*ptrA)[j]. #include #include #include #include "stdafx. void dimension(int &. cout<<endl. const int TAM = 10. _TCHAR* argv[]) { int a[TAM][TAM].h" "iostream" "iomanip" <conio.Universidad Nacional de Trujillo Ingeniería de Sistemas Computación Apuntadores Ejemplo 4. Escriba un programa que permita. int col) { for(int i=0. int _tmain(int argc. -9- . int fil. fil. } } Ing. cout.

const int TAM = 20. cout<<endl<<endl<<setw(35)<<"Listado ordenado por nombre" <<endl<<endl. int n.." <<endl<<"Presione una tecla para continuar. int edad. system("cls"). } void numeroPersonas(int &n) { do { cout<<"Numero de personas (1-"<<TAM<<"): ". } Ing.get(). }.h> <windows. reporte(d. haciendo la salvedad que en la estructura no se asigna un único valor sino tantos valores como miembros se hayan definido en ella. cin>>n. ingreso(d. Zoraida Yanet Vidal Melgarejo. }while(n<1 || n>TAM).n). int). haciendo uso de estructuras y apuntadores. cout<<endl<<endl<<setw(30)<<"Ingreso de datos"<<endl<<endl. cout<<endl<<setw(50)<<"Arreglo de estructuras"<<endl<<endl. ingresar el nombre y la edad de N personas. _getch(). int). ordenaPorNombre(d. Mg.h> <conio. n). ordenaPorNombre(datos [].h" "iostream" "iomanip" <string. reporte(datos []. _getch(). cout<<endl<<endl<<setw(30)<<"Listado original"<<endl<<endl. Computación Apuntadores Para trabajar sobre un arreglo de estructuras se aplican los mismos mecanismos que para trabajar con un arreglo de números enteros por ejemplo. Apuntadores y arreglos de estructuras. . Escriba un programa que permita. system("cls").. void void void void numeroPersonas(int &). #include #include #include #include #include #include "stdafx. int _tmain(int argc. n).Universidad Nacional de Trujillo Ingeniería de Sistemas 9.h> using namespace std.".n). Ejemplo 5. ingreso(datos []. struct datos { char nombre[30]. cout<<endl<<endl<<"Termino el ingreso de datos. _TCHAR* argv[]) { datos d[TAM]. numeroPersonas(n). int). reporte(d. y que luego reporte dichos datos ordenados ascendentemente por el nombre. return 0.10 - . cin.

temp. } ++pAux. } ++pD.nombre.nombre<<setw(5)<<pD[i].get(). i++) cout<<setw(5)<<""<<setw(30)<<pD[i]. for(int i=0. pAux = pD. int n) { for(int i=0. i++) { for(int j=i.edad <<endl. j++) { if(strcmp(pD->nombre. pAux = pD. int n) { datos *pAux. *pAux = temp. pAux->nombre)>0) { temp = *pD. Zoraida Yanet Vidal Melgarejo. cout<<"Nombre: ". j<n. int n) { cout. cout<<resetiosflags(ios::left). *pD = *pAux. Los apuntadores pueden estructurarse en arreglos como cualquier otro tipo de dato. i<n. i<n-1. } } void ordenaPorNombre(datos *pD. La declaración para un arreglo de punteros a enteros de tamaño 10 es: int *a[10].Universidad Nacional de Trujillo Ingeniería de Sistemas void ingreso(datos *pD. Arreglos de apuntadores.getline(pD[i].edad. Para referirse al valor de num: *a[2] Ing. cin>>pD[i]. i<n. . i++) { cout<<endl<<"Datos de persona "<<(i+1)<<endl. Para asignar la dirección de una variable entera llamada num al tercer elemento del arreglo de punteros. 30). cout<<"Edad: ". Mg. for(int i=0. } } Computación Apuntadores void reporte(datos *pD. cin.11 - . cout<<setw(5)<<""<<setw(30)<<"Nombre"<<setw(5)<<"Edad"<<endl. se debe indicar: a[2] = &num. } 10. cin.setf(ios::left).

// Se declara apuntador a otro apuntador. el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes. Dado que la variable ptr1 es de tipo apuntador a entero de 4 bytes. // El apuntador ptr2 recibe la dirección de ptr1. se agrega un byte a la dirección y para un apuntador a int o a float se agregan 4 bytes. considerando siempre que lo que se suma y se resta son posiciones de tipo base: int *ptr1. apunta a la dirección de memoria del siguiente elemento de su tipo base. La razón por la cual se asocia un apuntador a un tipo de dato. luego de la suma. Zoraida Yanet Vidal Melgarejo. De esta forma si a un apuntador a float se le suma 2. **ptr2. Por lo tanto para un apuntador a un char. Apuntadores a apuntadores.Universidad Nacional de Trujillo Ingeniería de Sistemas 11. en donde el bloque está en función del tamaño del dato. *ptr1. // Se declara un apuntador a un entero. apunta a la posición del elemento anterior. La forma de asignarles valores sería: int var1. que cuando se incrementa un apuntador. apuntará 36 (9 x 4) bytes mas allá del valor inicial. Mg. También se les puede sumar y restar enteros a los apuntadores. se incrementa el apuntador por un “bloque” de memoria. 12. De tal forma. ptr1 = &var1. int *ptr1. ptr2 = &ptr1. Cada vez que la variable apuntador se incrementa. var1 = 10. ptr1 = ptr1 + 9. // El apuntador ptr1 recibe la dirección de var1.12 - . int **ptr2. es por que se debe conocer en cuantos bytes está guardado el dato. Cada vez que la variable apuntador disminuye. Denominado también indirección múltiple o encadenamiento de punteros. Computación Apuntadores Sólo se puede realizar suma y resta de apuntadores. Aritmética de apuntadores. . // Se asigna valor a var1. Ing.