Los punteros son una de las herramientas más útiles, pero a la vez, complejas y peligrosas de C. Un puntero es una variable entera sin signo que almacena la dirección de memoria de otra variable o dato. Para ser más exactos, almacena la dirección del primer byte que la variable ocupa en memoria. Esto es importante tenerlo en cuenta, a la hora de entender la aritmética de punteros.
La declaración de un puntero se realiza de la siguiente manera:
[cualificador] tipo *nombre;
Donde cualificador recordemos que puede ser extern o static y es opcional, sólo será necesario si la variable queremos que se comporte de la forma indicada para esos cualificadores. Tipo es cualquiera de los vistos ahora y alguno que añadiremos más adelante, e indica el tipo de la variable almacenada en la dirección de memoria apuntada por el puntero. El caracter "*" es el que indica que la variable que estamos definiendo es un puntero. Y, por último, nombre es el identificador de la variable puntero.
Es importante tener en cuenta que el hecho de declarar un puntero implica que se reservará espacio en memoria para guardar un puntero (dirección de memoria), no para el tipo de datos al que apunta.
Ejemplo.
La declaración:
int *i;
implica que se está declarando un puntero que vamos a llamar "i" que apunta a un entero, es decir, la variable i contendrá una dirección de memoria central donde se almacenará un entero. Más exactamente, contiene la dirección de memoria del primer byte del entero almacenado. Con esta declaración se reserva espacio para almacenar "i", es decir, el puntero, no para almacenar el entero. Esto último se tendrá que hacer con la declaración correspondiente, como veremos en siguientes ejemplos.
De igual manera se declararían punteros a tipos float, long, char, etc.
Existen dos operadores especiales relacionados con punteros:
Ejemplo_punt_1. De uso de los operadores "&" y "*" (a partir de aquí en numerosos ejemplos sólo mostraremos la parte de código que nos interesa, se deja para el alumno añadir las líneas necesarias para crear un programa que pueda ser compilado).
int *i, j;
j=5;
i=&j; // Asignamos a i la dirección de j
printf ("dirección de j (en hexadecimal)=%x\n",i);
printf ("valor de j=%d\n",*i); // Acceso a valor mediante dirección
printf ("valor de j=%d\n", j); // Acceso a valor mediante variable, como lo hemos venido haciendo hasta ahora
Un valor especial que se le puede asignar a un puntero es el valor NULL (constante definida con valor 0 en el fichero de cabecera stddef.h, fichero incluido en el stdio.h). Un puntero al que se le asigna este valor significa que no apunte a ninguna posición de memoria, se le puede considerar como "puntero vacio".
Existen sólo dos operaciones que se puedan usar con punteros: la suma y la resta. Más concretamente las operaciones permitidas son el incremento/decremento, la suma/resta de valores enteros y la suma/resta de punteros.
Las operaciones más útiles son el incremento/decremento, como veremos al hablar de vectores. Cada vez que se incrementa un puntero, apunta a la posición de memoria del siguiente elemento de su tipo base. Cada vez que se decrementa, apunta a la posición del elemento anterior. Los punteros aumentan o decrecen la longitud del tipo de datos a los que apuntan.
Ejemplo_punt_2. El puntero x apunta a un float (4 bytes). Supongamos que su valor es dir. Entonces la expresión x+1 devuelve el valor dir+4, no el dir+1. El siguiente programa permite ver esto:
float x[3] = {1.1, 2.2, 3.3};
float *punt = x;
printf ("Dirección de memoria del primer elemento de x: %x y del segundo: %x\n",x,x+1);
printf ("Dirección de memoria del primer elemento de x: %x y del segundo: %x\n",punt,(punt+1));
printf ("Valor almacenado en la dirección de memoria %x: %f y en %x: %f\n",x,*x,x+1,*(x+1));
printf ("Valor almacenado en la dirección de memoria %x: %f y en %x: %f\n",x,x[0],x+1,x[1]);
Comentario: dado que un puntero es una dirección de memoria, su valor se visualiza de manera más compacta es hexadecimal.