ESTRUCTURAS DINÁMICAS DE DATOS Apuntadores Un apuntador es una variable cuyo valor es la dirección de memoria de otra variable. Se dice que un apuntador “apunta” a la variable cuyo valor se almacena a partir de la dirección de memoria que contiene el apuntador. Por ejemplo, si un apuntador p almacena la dirección de una variable x, se dice que “p apunta a x”. Los símbolos * y & pueden ser usados para el contenido y la dirección de una variable, respectivamente. Por ejemplo, si "x" es una variable de tipo "int" y "y" es una variable de tipo "int" y "px" es una variable de tipo "apuntador a int" entonces, se podría hacer lo siguiente: Asignar la dirección de "x" al apuntador "px" px = &x; Aumentar en 1, el valor apuntado por "px" (es decir, el valor de "x") y asignarlo a la variable "y". y = *px+1; Aumentar en 1, el valor del apuntador "px" (es decir, el valor de una variable desconocida) y asignarlo a la variable "y". Aumentar en 1 el valor apuntado por "px" (es decir, el valor de "x"). *px+=1; o bien (*px)++; Notar que en este último ejemplo es necesario usar paréntesis, debido a que, aunque la prioridad de "*" y "++" es igual, la asociatividad es de DERECHA a IZQUIERDA. Así, sin los paréntesis, la expresión "*px++;" se entendería como: "*(px++);". Qué son los punteros Ahora ya estamos en condiciones de ver lo que es un puntero. Un puntero es una variable un tanto especial. Con un puntero podemos almacenar direcciones de memoria. En un puntero podemos tener guardada la dirección de una variable. Veamos la diferencia entre una variable puntero y las variables "normales". En el dibujo anterior tenemos una representación de lo que puede ser la memoria del ordenador. Cada casilla representa un byte de la memoria. Y cada número es su dirección de memoria. La primera casilla es la posición 00001. La segunda casilla es la posición 00002, y así sucesivamente. Supongamos que ahora declaramos una variable: char numero = 43. El ordenador guardaría, por ejemplo, esta variable en la posición 00003. Esta posición de la memoria queda reservada y ya no la puede usar nadie más. Además, esta posición a partir de ahora se denomina número. Como le hemos asignado el valor 43, el valor 43 se almacena en la posición de memoria 00003. Referenciación La referenciación es la obtención de la dirección de una variable. En C y C++ esto se hace a través del operador ‘&’, aplicado a la variable a la cual se desea saber su dirección. Nótese que se trata de un operador unario. Ejemplo: Código C int x = 25; cout << "La dirección de x es: " << &x << endl; Para declarar un apuntador se especifica el tipo de dato al que apunta, el operador ‘*’, y el nombre del apuntador. La sintaxis es la siguiente: <tipo de dato apuntado> *<identificador del apuntador> A continuación se muestran varios ejemplos: Código C int *ptr1; // Apuntador a un dato de tipo entero (int) char *cad1, *cad2; // Dos apuntadores a datos de tipo carácter (char) float *ptr2; // Apuntador a un dato de tipo punto-flotante (float) Asignación de apuntadores Se pueden asignar a un apuntador direcciones de variables a través del operador de referenciación (‘&’) o direcciones almacenadas en otros apuntadores. Ejemplos: Código C y C++ int i = 5; int *p, *q; p = &i; // Se le asigna a ’p’ la dirección de ’i’ q = p; // Se le asigna a ’q’ la dirección almacenada en ’p’ (la misma de ’i’) Desreferenciación de apuntadores La Desreferenciación es la obtención del valor almacenado en el espacio de memoria donde apunta un apuntador. En C y C++ esto se hace a través del operador ‘*’, aplicado al apuntador que contiene la dirección del valor. Nótese que se trata de un operador unario. Ejemplos: Código C int x = 17, y; int *p; p = &x; cout << "El valor de x es: " << *p << endl; // Imprime 17 y = *p + 3; // A ’y’ se le asigna 20 Otro ejemplo. Un operador de indirección o de desreferencia: El operador * aplicado al nombre de un apuntador indica el valor de la variable apuntada; Regresa el valor del objeto hacia el cual su operando apunta, es decir un apuntador, ejemplo: main() { int x,y; int *py; y = 5; *py = y; x = *py + 5; printf(''%d %d nn'',*py,x); } Veamos con un ejemplo en C la diferencia entre todos estos conceptos Es decir: int x = 25, *pint; pint = &x; La variable pint contiene la dirección de memoria de la variable x. La expresión: *pint representa el valor de la variable (x) apuntada, es decir 25. La variable pint también tiene su propia dirección: &pint Otros ejemplos: float *q; /*apuntador hacia un flotante*/ char *z; /*puntero que contiene la dirección de una variable que guarda un carácter */ Para referirnos a un valor a través de un apuntador, lo hacemos mediante un proceso llamado indirección. Por ejemplo, para mandar a impresión el valor entero, hacia el cual a punta “p”, sería así: Printf(“%d”,*p); #include <stdio.h> main() { int numero; int *punt; numero = 43; punt = &numero; printf( "Dirección de numero = %p, valor de numero = %i\n", punt, numero ); } Vamos a analizar línea a línea: En la primera, int numero;, reservamos memoria para numero (supongamos que queda como antes, en la posición 00003). Por ahora numero no tiene ningún valor. Siguiente línea: int *punt;, reservamos una posición de memoria para almacenar el puntero. Lo normal es que a medida que se declaran variables se guarden en posiciones contiguas. De modo que quedaría en la posición 00004. Por ahora punt no tiene ningún valor, es decir, no apunta a ninguna variable. Esto es lo que tenemos por ahora: Tercera línea: numero = 43;. Aquí estamos dando el valor 43 a número. Se almacena 43 en la dirección 00003, que es la de número. Cuarta línea: punt = &numero;. Por fin damos un valor a punt. El valor que le damos es la dirección de numero (ya hemos visto que & devuelve la dirección de una variable). Así que punt tendrá como valor la dirección de numero, 00003. Por lo tanto tenemos: Cuando un puntero tiene la dirección de una variable se dice que ese puntero apunta a esa variable. La declaración de un puntero depende del tipo de dato al que queramos apuntar. En general, la declaración es: tipo_de_dato *nombre_del_puntero; Veamos el siguiente ejemplo. Queremos comprobar si dos variables son iguales usando punteros: #include <stdio.h> main() { int a, b; int *punt1, *punt2; a = 5; b = 5; punt1 = &a; punt2 = &b; if ( punt1 == punt2 ) printf( "Son iguales\n" ); } Diseñe un programa, que sume dos variables de tipo entero, por medio de apuntadores. #include <stdio.h> #include <conio.h> main() { int a, b, c; int *p1, *p2, *p3; /*declaración de los punteros */ printf("Ingrese el valor de a:\n"); scanf("%d", &a); printf("Ahora el valor de b:\n"); scanf("%d", &b); c=a+b; printf("a+b=%d\n", c); /*Asignamos las direcciones a los punteros correspondientes/ p1=&a; p2=&b; /*Ahora desreferenciamos el valor de los punteros para sumarlos */ printf("*p1 + *p2=%d\n", *p1+*p2); p3=&c; printf(" Dirección de a es %p\n Dirección de b es %p\n Y la de c es %p\n\n", p1, p2, p3); getch(); return 0; }