lunes, 26 de mayo de 2014

4.2 Definicion de apuntador


 1. Que son los apuntadores

Un apuntador es una variable que almacena una dirección de memoria. Lo primero que se debe hacer al trabajar con apuntadores es declararlos, la forma como se declara un apuntador se muestra a continuación:
Donde1:
1 Las cosas que se encuentran entre corchetes son opcionales.
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 combinación de los modificadores de tipo const, volatile y restrict.
Nombre: Nombre del apuntador.
Valor inicial: Valor inicial del apuntador.
Por ejemplo, supóngase que se declaró un puntero a una variable la cual tiene el valor de 5 tal y como se muestra a continuación:
 int theVariable = 5;
int *pPointer = &theVariable;
Como se puede apreciar el valor almacenado en el apuntador es la dirección de memoria de la variable a la cual está apuntando. Esto fue posible gracias al operador dirección (&). Con base en la figura anterior se construyó la siguiente tabla para clarificar su uso:
                                    l       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 dirección en la cual se encuentra la variable en cuestión. Como una variable puede ocupar más de 1 byte, el valor resultante es el byte asociado a la dirección 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 continuación:
2. Referenciando apuntadores

Consiste en asociar el apuntador a una dirección específica, para esto se suele usar el operador & para obtener la dirección de la variable en cuestión. A continuación se muestra la forma como normalmente se hace esto:
apuntador = &variable;
También es posible referenciar un apuntador pasándole 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 operación en memoria el programa sacara un error. Un puntero que no ha sido inicializado se conoce como Wild pointer.
En la siguiente figura se ilustra un poco mejor lo anterior:
int i,j;
int *p; //Apuntador a un entero

Hasta el momento solo se ha declarado el apuntador pero no se ha referenciado, en la siguiente figura se muestra el efecto de referenciar el apuntador: p = &i;

Es posible que varios punteros estén apuntando a un mismo lugar de memoria: 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 (*).
El valor del lugar de memoria apuntado se obtiene de la siguiente manera:
variable = *apuntador;
Ahora si lo que se desea hacer es escribir en el lugar de memoria apuntado se hace lo siguiente:
*apuntador = valor;
La siguiente figura muestra el resultado de desreferenciar un apuntador:
*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 dirección valida.
4. Usos de los apuntadores

Los apuntadores se usan principalmente para 3 cosas:
 Crear estructuras de datos dinámicas.
 Manejar parámetros variables pasados a funciones.
 Acceder de a los diferentes elementos de arreglos o estructuras.

A continuación se trata con más detalle cada una de estas aplicaciones.
4.1 Funciones y apuntadores
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 están pasando a la función 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. También, mediante el uso de apuntadores, es posible superar la restricción que se tiene en la cual una función no puede retornar más de un elemento; así, por medio de referencias es posible retornar un array por ejemplo.
Para indicar que una función será pasada por referencia, se emplean apuntadores en la cabecera de la función, esto porque lo que se pasa como argumento es la dirección de memoria. Por ejemplo:
tipo_retorno f(tipo_1 *pName_1,tipo_2 *pName_2,...,tipo_N *pName_N)
Para aterrizar un poco más lo anterior, supongamos esta función:
void swap(int *px, int *py) {
int temp;
cout << "Swap. Before swap, *px: " << *px <<
" *py: " << *py << endl;
temp = *px;
*px = *py;
*py = temp;
cout << "Swap. After swap, *px: " << *px <<
" *py: " << *py << endl;
}
Como se pueden notar en la definición de la función anterior, en este caso ambos argumentos son pasados por referencia.
Ahora en lo que respecta a la invocación si lo que se pasa es como parámetro es una variable como tal se debe hacer uso del operador & para obtener la dirección 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 dirección de memoria. La siguiente tabla ilustra esto:
Caso
Invocacion
Observaciones
Se está pasando una variable a una función que se llama por referencia
int x = 5, y = 10;
swap(&x,&y);
Es necesario usar el operador & para obtener la dirección de memoria de las variables y así poder inicializar lo apuntadores que funcionan como argumentos.
Se está pasando apuntador a una función que se llama por referencia
int x = 5, y = 10;
int *px = &x, *py;
py = &y;
swap(px,py);
Como lo que se pasan son apuntadores previamente inicializados, estos ya tienen la dirección de memoria de la variable que será pasada como argumento de la función, por lo tanto no es necesario usar el operador &.
¿Qué sucede si lo que se pasa como argumento es lo resaltado?
int x = 5, y = 10;
int *px = &x, *py;
py = &y;
swap(&px,&py);
Codifique y compile el siguiente código:
// Demuestra el uso de funciones por referencia
#include <sdtio.h>
void swap(int *x, int *y);
void swapv(int x, int y);
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;
}
void swapr(int *px, int *py) {
int temp;
printf("Swapr. Antes del swap; *px: %d, *py: %d\n",*px,*py);
temp = *px;
*px = *py;
*py = temp;
printf("Swapr. Despues del swap; *px: %d, *py: %d\n",*px,*py);
}
void swapv(int x, int y) {
int temp;
printf("Swapv. Antes del swap; x: %d, y: %d\n",x,y);
temp = x;
x = y;
y = temp;
printf("Swapv. Despues del swap; x: %d, y: %d\n",x,y);
}

No hay comentarios.:

Publicar un comentario