Los punteros tienen dos cometidos. El primero es almacenar una dirección de memoria (ver sección 5.4). El segundo es utilizar la dirección de memoria almacenada para acceder al dato al que apunta mediante una indirección (ver sección 5.2 es una indirección).
En C la indirección se denota con el operador *
seguido del nombre de una variable de tipo puntero. Su significado es
“accede al contenido al que apunta el
puntero”. Desafortunadamente, este operador coincide con el utilizado
para denotar los tipos de datos punteros y para declarar este tipo de
variables. Es muy importante tener una distinción muy clara entre estos dos
usos del operador. Al traducir un programa, el compilador no comprueba ni
que el puntero contiene una dirección de memoria correcta, ni que en esa
dirección de memoria hay un dato correcto. Esta característica hace que el
diseño de programas con punteros sea complejo.
El siguiente programa muestra el uso del operador de indirección (fichero pointer_example_2.c):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> int main(int argc, char** argv) { int num1, num2; int *ptr1, *ptr2; ptr1 = &num1; ptr2 = &num2; num1 = 10; num2 = 20; *ptr1 = 30; *ptr2 = 40; *ptr2 = *ptr1; return 0; } |
La línea 13 asigna el valor 30 a la dirección de memoria
almacenada en ptr1
mediante una indirección. Como el puntero
tiene la dirección de num1
(asignada en la línea 7) esta linea
es análoga a asignar el valor 30 directamente a
num1
. Análogamente, la línea 14 asigna el valor 40 a la
dirección a la que apunta ptr2
. Esta asignación es equivalente
a asignar el valor 40 a num2
. La línea 16 contiene dos
indirecciones. La expresión a la derecha del signo igual obtiene el dato en
la posición indicada por la dirección almacenada en ptr1
(la
dirección de num1
) y este dato se almacena en la posición
indicada por el puntero ptr2
(la dirección de
num2
). Al terminar el programa, la variable num2
contiene el valor 30 a pesar de que no ha sido asignado directamente.
La siguiente figura muestra la evolución de estas cuatro variables a lo largo del programa. De nuevo, las direcciones de memoria en las que están almacenadas son arbitrarias.
La coincidencia entre el operador de indirección y el de
definición de tipo puntero “*
” puede dificultar la
comprensión del código. Por ejemplo, la siguiente línea
int *r = *p;
contiene dos operandos “*
”. El
primero se define el tipo “puntero a entero” y va seguido de la
declaración de la variable “r
”. El segundo es una
indirección que se aplica a la dirección almacenada en el puntero
“p
”.
Supongamos que se ha definido una estructura con nombre
“struct s
”. Aplicando la regla de definición de
tipo puntero, un puntero a esta estructura tiene el tipo
“struct s *
”. En el siguiente fragmento de
código se define y declara una estructura, y luego se declara un puntero
que se inicializa a su dirección.
1 2 3 4 5 6 7 8 9 10 | struct s { int x; char c; } struct s element; struct s *pointer; pointer = &element; |
Cuando se realiza una indirección con pointer
es sólo para acceder a los campos de la estructura. Esta operación
requiere dos operadores, la indirección (operador
“*
” y el acceso a los campos de una estructura
(operador “.
”). La sintaxis es:
(*pointer).x = 10; (*pointer).c = 'l';
Estas dos operaciones en C se agrupan en el operador
“->
” que se sitúa entre el puntero y el nombre
del campo de la estructura. La notación equivalente, por tanto, es:
pointer->x = 10; pointer->c = 'l';
El operador “->
” se escribe
siempre a continuación de un puntero que apunta a una estructura, y
precede al nombre de uno de los campos de esa estructura. En el siguiente
programa se muestra como se pueden copiar los datos de una estructura a
otra utilizando este operador.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /* Definición de la estructura */ struct coordenadas { float x; float y; float z; }; int main() { /* Declaración de dos estructuras */ struct coordenadas location1, location2; /* Declaración de dos punteros */ struct coordenadas *ptr1, *ptr2; /* Asignación de direcciones a los punteros */ ptr1 = &location1; ptr2 = &location2; /* Asignación de valores a la primera estructura */ ptr1->x = 3.5; ptr1->y = 5.5; ptr1->z = 10.5; /* Copia de valores a la segunda estructura */ ptr2->x = ptr1->x; ptr2->y = ptr1->y; ptr2->z = ptr1->z; return 0; } |
Copia y pega el contenido del programa anterior en un
fichero de texto en tu entorno de desarrollo. Compila y ejecuta. Define
una segunda estructura e incluye un puntero a ella como campo de la
estructura “coordenada
”. Escribe código para
manipular esta nueva estructura.