Documente Academic
Documente Profesional
Documente Cultură
Objetivos
Indice
Contenido
Un apuntador es una variable que almacena una direccin de memoria. Lo primero que se debe
hacer al trabajar con apuntadores es declararlos, la forma como se declara un apuntador se
muestra a continuacin:
Donde1:
Tipo: Tipo de dato al cual se desea apuntar, puede ser un tipo de dato simple (char, int, etc) o
un tipo de dato complejo como una estructura o una clase.
Modificadores del tipo: Puede contener cualquier combinacin de los modificadores de tipo
const, volatile y restrict.
Nombre: Nombre del apuntador.
Valor inicial: Valor inicial del apuntador.
Por ejemplo, supngase que se declar un puntero a una variable la cual tiene el valor de 5 tal y
como se muestra a continuacin:
int theVariable = 5;
int *pPointer = &theVariable;
1
Las cosas que se encuentran entre corchetes son opcionales.
En la siguiente figura se muestra el efecto en memoria de la declaracin anterior:
Significado Valor
theVariable Contenido de theVariable 5
&theVariable Direccion de theVariable 101
pPointer Contenido del apuntador pPointer 101
&pPointer Direccion del apuntador pPointer 106
Como se puede notar de la tabla anterior el valor obtenido con el operador & es la direccin en la
cual se encuentra la variable en cuestin. Como una variable puede ocupar ms de 1 byte, el valor
resultante es el byte asociado a la direccin base de la variable.
Ahora bien, con el apuntador es posible acceder a cualquier lugar de memoria y modificar su valor.
Para ello se tiene que referenciar y desreferenciar el apuntador. Esto se describe a continuacin:
2. Referenciando apuntadores
Consiste en asociar el apuntador a una direccin especfica, para esto se suele usar el operador &
para obtener la direccin de la variable en cuestin. A continuacin se muestra la forma como
normalmente se hace esto:
apuntador = &variable;
Tambin es posible referenciar un apuntador pasndole el valor que se tiene en otro apuntador.
Note que no se hizo uso del operador & en este caso:
apuntador1 = apuntador2;
Todo apuntador debe inicializarse antes de usarse. Si esto no se hace, cuando intente usarlo para
hacer alguna operacin en memoria el programa sacara un error. Un puntero que no ha sido
inicializado se conoce como Wild pointer.
int i,j;
int *p; //Apuntador a un entero
p = &i;
int i;
int *p,*q,*r;
p = &i;
q = &i;
r = p;
3. Deseferenciando un apuntador
Para poder acceder al lugar de memoria que est siendo apuntado por el puntero y realizar
operaciones de lectura y escritura sobre este el puntero se debe desreferenciar. Para ello se hace
uso del operador desreferencia (*).
variable = *apuntador;
Ahora si lo que se desea hacer es escribir en el lugar de memoria apuntado se hace lo siguiente:
*apuntador = valor;
*p = 5;
Como se puede notar de la figura anterior, es posible modificar el valor de i desde el apuntador.
Vale resaltar que todo apuntador antes de ser desreferenciado debi haber sido previamente
inicializado con una direccin valida.
Existen dos maneras de hacer llamados a funciones, por referencia y por valor. Cuando se realiza
un llamado por valor; se trabaja sobre una copia de la variable pasada como argumento y por lo
tanto la variable original (la que se pas como argumento) no se modifica. Por otro lado, cuando
se realiza una llamada por referencia al estar accediendo al lugar de memoria en el que se
encuentra la variable pasada como argumento es posible modificar el valor original de la variable
pasada como argumento.
El paso de funciones por referencia es de extrema utilidad cuando los argumentos que se estn
pasando a la funcin son pesados ya que esto evita que se tengan que hacer copias de dichos
argumentos que en el peor de los casos pueden ocasionar que el programa colapse por llenar
stack. Tambin, mediante el uso de apuntadores, es posible superar la restriccin que se tiene en
la cual una funcin no puede retornar ms de un elemento; as, por medio de referencias es
posible retornar un array por ejemplo.
Para indicar que una funcin ser pasada por referencia, se emplean apuntadores en la cabecera
de la funcin, esto porque lo que se pasa como argumento es la direccin de memoria. Por
ejemplo:
Como se pueden notar en la definicin de la funcin anterior, en este caso ambos argumentos son
pasados por referencia.
Ahora en lo que respecta a la invocacin si lo que se pasa es como parmetro es una variable
como tal se debe hacer uso del operador & para obtener la direccin de dicha variable y as
inicializar el apuntador que funciona como argumento. Por otro lado si lo que se est pasando es
un apuntador a una variable, no es necesario usar el operador & ya que el valor almacenado en
este ser una direccin de memoria. La siguiente tabla ilustra esto:
int x = 5, y = 10;
int *px = &x, *py;
py = &y;
swap(&px,&py);
int main() {
int x = 5, y = 10;
printf("---------------------------------------------------\n");
printf("Llamada por valor \n");
printf("Main. Antes del swap; x: %d, y: %d \n");
swapv(x,y);
printf("Main. Despues del swap; x: %d, y: %d \n");
printf("---------------------------------------------------\n");
printf("Llamada por referencia \n");
printf("Main. Antes del swap; x: %d, y: %d \n");
swapr(&x,&y);
printf("Main. Despues del swap; x: %d, y: %d \n");
printf("---------------------------------------------------\n");
return 0;
}
Una funcin tambin puede retornar un apuntador cuando es invocada, para hacer esto, en la
definicin y declaracin de la funcin se debe indicar que la funcin retornara un apuntador lo
cual se hace precediendo el nombre de la funcin por un asterisco. A continuacin se muestra la
forma que debe llevar la funcin para este caso:
tipo_retorno *f(parametros...)
Note que lo realimente importante es que se declar un apuntador a un tipo de dato especfico, se
inicializo y luego se retorn este, en general en la definicin de la funcin se sigue la siguiente
plantilla:
En lo que respecta a la declaracin de la funcin (prototipo de la funcion), esta puede ser hecha de
la siguiente forma:
O tambin puede ser de la siguiente forma, sin colocar los nombres de las variables que sirven
como argumentos:
#include <stdio.h>
int main() {
int a[6] = {1,2,5,9,-1,3};
int *p;
p = mayor(a,5);
printf("El elemento mayor del vector es: %d\n",*p);
return 0;
}
Como los apuntadores son variables es posible realizar operaciones matemticas sobre ellos, sin
embargo debido a que lo almacenado en estos son direcciones de memoria no todas las
operaciones convencionales que se podran hacer sobre una variable normal son posibles. Las
operaciones validas son:
Aadir o sustraer un entero de un apuntador. Esto hace que el puntero apunte a otro lugar de
memoria diferente al que inicialmente estaba apuntando esto debido a la modificacin de lo
que se encuentra almacenado en este.
Sustraer un apuntador de otro: Cuando se realiza esta operacin, los dos apuntadores deben
ser del mismo tipo.
Comparar dos apuntadores. La comparacin es comnmente empleada para comparar
cualquier puntero con el puntero a NULL usando los operadores de igualdad (== o !=).
Las tres operaciones anteriormente descritas son generalmente tiles para apuntadores que se
refieren a los elementos de un array. Recordemos que un array consiste de un conjunto de
variables del mismo tipo las cuales pueden ser accedidas bajo un mismo nombre. Cuando se
declara un array lo que sucede en memoria es que se reservan un conjunto de posiciones
contiguas en memoria tal y como se muestra en la siguiente figura:
Para ilustrar lo anterior suponga lo que tiene dos apuntadores, p1 y p2 los cuales estn apuntando
a los elementos de un array a:
Si p1 apunta al elemento i del array (a[i]), y n es un entero, entonces la expresin p2 = p1 + n
hace que p2 apunta al elemento a[i+n]. (Se asume que i+n est dentro del ndice del array).
La resta p2 p1 da el nmero de elementos del array entre los dos apuntadores.
La comparacin p1 < p2 es cierta si el elemento referenciado por p2 tiene un ndice ms
grande que el referenciado por p1, de otro lado la comparacin es falsa.
Lo anterior muestra que existe una relacin entre la forma de escribir un array con subndices y
escribirlo con apuntadores aritmtica de apuntadores, esto implica que:
El nombre de un arreglo es realmente un apuntador al primer elemento en el array, asi si a es
un arreglo adimensional entonces la direccin del primer elemento del array es &a[0] o
simplemente a.
La direccin del elemento i del array puede ser expresada como &a[i] o como a + i, por lo
tanto existen dos manera de escribir la direccin de cualquier elemento del array.
a[i] o *(a+i) representan el contenido que hay en la direccin en cuestin.
El nmero de bytes de memoria asociados con cada elemento del array depender del tipo de
dato empleado en la declaracin (1 byte para dados tipo char o 4 para datos tipo doubl por
ejemplo). La siguiente figura clarifica lo anterior:
Es posible acceder a cada uno de los elementos del arreglo por medio del ndice o de manera
alternativa usando apuntadores y es all donde entra en juego la aritmtica de punteros ya que por
medio de las operaciones de adicin y sustraccin nos podemos mover a las diferentes posiciones
del array para luego poder acceder a sus elementos. Para entender ms esto suponga que tiene la
siguiente porcin de cdigo:
int a[6]={1,0,4,7,8,10};
int i,suma = 0;
a[0]=2;
a[3]=10;
for(i = 1;i<6;i++) {
if(i%2==0) {
a[i] = -a[i];
}
else {
a[i]=a[i]+1;
}
}
Como se puede notar del cdigo anteriormente mostrado, el acceso al array fue haciendo uso de
subndices, a continuacin se muestra la versin alternativa haciendo uso de apuntadores:
int a[6]={1,0,4,7,8,10};
int *ptr; // Declaracion del apuntador
ptr = &a[0]; // Inicializacion del apuntador. (ptr = a)
*ptr = 2; // a[0] = 2
ptr = &a[3]; // Ahora ptr esta apuntando al elemento a[3]
*ptr=10; //a[3] = 10
ptr = &a[1]; // Ahora ptr esta apuntando al elemento a[1]
for(i = 1;i < 6;i++) {
if(i%2==0) {
*ptr = -(*ptr);
}
else {
*ptr = *ptr + 1;
}
ptr++; //Cambio del valor del apuntador para barrer
// el arreglo
}
Pregunta:
Con que valores queda el vector a tras la ejecucin del cdigo anteriormente mostrado?
El resultado de ejecutar los dos cdigos anteriormente mostrados es el mismo. Observe dos cosas:
Para barrer el array se modific el valor almacenado en el apuntador.
Para acceder a un elemento del array (ya sea para leer o para escribir) se desreferencio el
apuntador con el operador asterisco (*).
for ( ; a < last; ++a ) // Walk the pointer a through the array.
{
minPtr = a; // Find the smallest element
for ( p = a+1; p <= last; ++p ) // between a and the end of the array.
if ( *p < *minPtr )
minPtr = p;
swapf( a, minPtr ); // Swap the smallest element
} // with the element at a.
}
As como en el caso de los arreglos de una dimensin, es posible representar los arreglos
multidimensionales con una notacin de punteros equivalente.
En el caso de las matrices de dos dimensiones, cuando estas se almacenan en memoria, la forma
como la memoria se llena es fila por fila, es decir si se tiene una matriz de n filas por m columnas
las primeras n posiciones en memoria corresponden a los n elementos de la primera fila, las n
posiciones siguientes corresponden a los n elementos de la siguiente fila y as sucesivamente hasta
que todas las filas son ocupadas (n*m posiciones de memoria). La siguiente figura muestra esto:
Ahora bien, un arreglo de dos dimensiones puede ser tratado como un arreglo de una dimensin
cuyos elementos son arreglos de una dimensin (las filas) tal y como lo sugiere la siguiente figura:
Lo anterior hace posible que se pueda definir un arreglo de dos dimensiones como un apuntador a
un grupo de arreglos de una dimensin, de modo que una declaracin de un arreglo bidimensional
puede ser hecha de la siguiente manera:
T *(ptVar)[n];
Donde T est asociado al tipo de dato (el cual puede ser simple o compuesto) y n es el nmero de
filas del array bidimensional. La expresin anterior puede ser por lo tanto una alternativa a la
declaracin tpica de matrices por ejemplo para el caso de una matriz de n filas por n columnas
como la siguiente:
T mat[n][m];
La expresin anterior se puede generalizar a arreglos de ms elementos tal de modo que para una
matriz de dimensin N:
T mat[val1]...[val_N];
T *(ptVar)[val_2][val_3]...[val_N];
Donde T se refiere al tipo de dato y las expresiones val_1, val_2, , val_N se refieren al nmero
mximo de elementos asociados con cada uno de los N subndices del array.
Otra cosa importante es la presencia del parntesis, este es necesario ya que si no est, no nos
estaramos refiriendo a un apuntador a un grupo de arrays sino a un array de apuntadores, esto
porque los [] tienen mayor precedencia que el *. As segn lo anterior:
Ejemplo 1:
Se desea crear un array de bidimensional de 10 filas por 20 columnas llamado x esto puede ser
hecho, as:
O as:
int x[10][20];
Ejemplo 2:
Considere una matriz de tres dimensiones con la siguiente declaracin:
float t[10][20][30];
float (*t)[20][30];
Ejemplo 3:
Supongase que se tiene un array entero de 10x20 y se desea acceder al elemento perteneciente a
la fila 2 y la columna 5 (x[2][5]) . La forma de acceso es la mostrada a continuacin:
*(*(x+2)+5);
Para acceder a dicho lugar de memoria es necesario tener en cuenta una serie de cosas:
x+2: Puntero al vector correspondiente a la fila 2.
*(x+2): Apuntador al primer elemento de la fila 2.
*(x+2)+5: Apuntador al elemento 5 en la fila 2.
*(*(x+2)+5): Elemento de la columna 5 y la fila 2, propiamente x[2][5].
T mat[val_1][val_2];
T *ptVar[val_1];
T *(ptVar)[val_2][val_3]...[val_N];
T *(ptVar)[val_1][val_2]...[val_N-1];
Aqu T se refiere al tipo de dato y las expresiones val_1, val_2, , val_N se refieren al nmero
mximo de elementos asociados con cada uno de los N subndices del array. Note que a diferencia
del caso anterior ya no se emplean parntesis en este tipo de declaracin por lo que el array
contendr apuntadores al tipo especfico definido en la declaracin.
Ejemplo 1:
Supngase que se tiene una matriz bidimensional x de 10 filas por 20 columnas. Esta definida
como un vector de apuntadores sera:
int *x[10];
Aqu, x[0] apuntara al principio de la primera fila, x[1] al principio de la segunda, observe que a
diferencia del caso anterior no fue necesario declarar el nmero de elementos de cada columna
de manera explcita. La siguiente figura muestra el efecto de la anterior declaracin con ms
detalle:
Para acceder a un elemento individual del arreglo, tal como x[2][5] de la siguiente manera:
int *(x[2]+5);
Donde:
x[2]: Es un apuntador al primer elemento de la fila 2.
x[2] + 5: Es un apuntador al elemento 5 dentro de la fila 2.
*(x[2]+5): Se refiere al contenido del elemento de la fila 2 y la columna 5, es decir x[2][5].
Ejemplo 2:
Suponga que se tiene un array tridimensional de nmeros de punto flotante de dimensiones
10x20x30. La forma de expresar este array como un array de apuntadores bidimensional es:
float *t[10][20];
Lo cual genera un array de 200 apuntadores cada uno de los cuales esta apuntando a un vector
unidimensional. Ahora bien, si se desea acceder a un elemento individual tal como t[2][3][5], la
forma de accederlo es:
*(t[2][3]+5);
Los arreglos de apuntadores ofrecen a menudo un mtodo conveniente para almacenar cadenas
de caracteres. En esta situacin, cada elemento del array es un apuntador tipo carcter que indica
el principio de una cadena de caracteres separada, por lo tanto un array de n elementos puede
apuntar a diferentes cadenas de caracteres, de tal manera que cada cadena individual puede ser
referenciada por su correspondiente apuntador. La siguiente figura muestra este hecho:
Tambien es posible pasar apuntadores a funciones pero eso no lo vamos a tratar en este caso.
La siguiente tabla muestra una lista de equivalencias entre los valores almacenados en las
variables anteriormente creadas:
La anterior tabla implica que si yo quiero cambiar el valor de ch lo puedo hacer ya modificando la
variable como tal o desreferenciando el apuntador que la apunta o desreferenciando el apuntador
del apuntador que la apunta, a continuacin se muestran las tres equivalencias:
ch = A; *pch = A; **ppch = A
Ejercicio
Dado el siguiente cdigo fuente:
#include <iostream>
int main() {
char *materias[3]={"Quimica","Matematicas","Fisica"};
float *notasMateria[3];
float **nota;
float nQuimica[]={2,4,5};
float nMatematicas[4]={3.2,1,2.2,5};
float nFisica[] = {4.5,4.3,3.2,1.5,5};
notasMateria[0]=nQuimica;
notasMateria[1]=&nMatematicas[0];
notasMateria[2]=nFisica;
return 0;
}
Preguntas:
Modifique el cdigo anterior de tal manera que pueda imprimir el valor que hay en cada uno
de los elementos del vector de apuntadores y llene la siguiente tabla:
Variable Valor
nQuimica
nFisica
nMatematicas
notasMateria[0]
notasMateria[1]
notasMateria[2]
¬asMateria[0]
¬asMateria[1]
¬asMateria[2]
De cunto es el tamao reservado por el compilador para una variable tipo float (recuerde el
operador sizeof)?
Cul es el efecto que se obtendra al ejecutar cada una de las siguientes instrucciones?, Si
alguna de estas genera error explique por qu?
Instruccin Comentario
nFisica[0]=3.0; Lleva al elemento 0 del array nFisica el valor de 3.
*(nMatematicas+2) = 3.5;
*(&nQuimica[2]-1) = 2.2;
*(notasMateria[2]+4)=0;
*(notasMateria+2)=0.5;
*(¬asMateria[1]+2)=0.5;
Instruccin Instruccion
nFisica[0]=3.0; *(notasMateria[0])=3.0;
nMatematicas[3]=0.7;
nQuimica[2]= nQuimica[0]+ nQuimica[1];
nMatematicas[3]=nQuimica[2];
nFisica[0]=(nFisica[1]+nFisica[2])/2;
nMatematicas[2]=4.8;
nota = ¬asMateria[2];
nota = ¬asMateria[1];
cout << ¬asMateria[1]<<" "<<¬asMateria[2]<<" "<<nota<<endl;
*nota += 2;
cout << &nMatematicas[2] << " " <<*nota << endl;
**nota = 0.7;
Referencias
C in an Nutshell.
C++ in a Nutshell.
Sam teach yourself C++ in 21 days.
Shaums outline of programming with C.
A introduction to design patterns in C with Qt4.
C primer plus.
http://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxjYXJsb3NobWVuZ
G96YWN8Z3g6MjFkZTI1MWJjYjM1ZWZmOA
http://www.virtual.unal.edu.co/cursos/ingenieria/2001839/modulo4/cap_05/leccion_3.htm
https://users.cs.jmu.edu/bernstdh/web/common/lectures/
http://computer.howstuffworks.com/c.htm
http://pw1.netcom.com/~tjensen/ptr/ch9x.htm
http://home.earthlink.net/~momotuk/pointers.pdf
http://www.lysator.liu.se/c/c-faq/c-2.html
http://www.cs.cf.ac.uk/Dave/C/node12.html#SECTION001210000000000000000
http://www.technoplaza.net/programming/lesson9p2.php
http://icesecurity.org/programacion/C/cursoc9.html
http://www.csse.monash.edu.au/~dwa/Animations/index.html
http://www.u-
gakugei.ac.jp/~miyadera/Education/TeachingSystem/Animations/LinkedList/linkedlist.html
http://www.macs.hw.ac.uk/~rjp/Coursewww/Cwww/linklist.html
http://www.ehow.com/how_2056292_create-linked-list-c.html
http://www.codeproject.com/KB/cpp/linked_list.aspx
http://cslibrary.stanford.edu/103/
http://www.codeproject.com/KB/cpp/PointerArticle.aspx
http://www.enm.bris.ac.uk/staff/pjn/EMAT10920/lesson10a.pdf
http://cplus.about.com/od/learning1/ss/pointers2.htm
http://www.sethi.org/classes/comp217/lab_notes/lab_10_pointers.html
http://www.cs.uregina.ca/Links/class-info/115/10-pointers/
http://cboard.cprogramming.com/c-programming/124384-advanced-pointer-use.html
http://computer.howstuffworks.com/c30.htm
http://www.technoplaza.net/programming/lesson9p2.php
http://www.cs.cf.ac.uk/Dave/C/node12.html
http://cplus.about.com/od/learningc/ss/pointers2_8.htm
http://cslibrary.stanford.edu/102/PointersAndMemory.pdf
http://publications.gbdirect.co.uk/c_book/chapter5/pointers.html
http://www.math.northwestern.edu/~wphooper/code/c/examples/pointers/
http://idlastro.gsfc.nasa.gov/idl_html_help/Pointer_Examples.html
http://augustcouncil.com/~tgibson/tutorial/ptr.html
http://cplus.about.com/od/learning1/ss/pointers.htm
http://cplus.about.com/od/learningc/ss/pointers_9.htm
http://icesecurity.org/programacion/C/cursoc22.html#arriba