Cada variable de un programa tiene una direcci�n en la memoria del ordenador. Esta direcci�n indica la posici�n del primer byte que la variable ocupa. En el caso de una estructura es la direcci�n del primer campo. En los ordenadores actuales la direcci�n de inicio se considera la direcci�n baja de memoria. Como en cualquier caso las variables son almacenadas ordenadamente y de una forma predecible, es posible acceder a estas y manipularlas mediante otra variables que contenga su direcci�n. A este tipo de variables se les denomina punteros.
Los punteros C son el tipo m�s potente y seguramente la otra clave del �xito del lenguaje. La primera ventaja que obtenemos de los punteros es la posibilidad que nos dan de poder tratar con datos de un tama�o arbitrario sin tener que moverlos por la memoria. Esto puede ahorrar un tiempo de computaci�n muy importante en algunos tipos de aplicaciones. Tambi�n permiten que una funci�n reciba y cambie el valor de una variable. Recordemos que todas las funciones C �nicamente aceptan par�metros por valor. Mediante un puntero a una variable podemos modificarla indirectamente desde una funci�n cualquiera.
Un puntero se declara de la forma: tipo *nombre;
float *pf; PLANETA *pp; char *pc;
Para manipular un puntero, como variable que es, se utiliza su nombre; pero para acceder a la variable a la que apunta se le debe preceder de *. A este proceso se le llama indirecci�n. Accedemos indirectamente a una variable. Para trabajar con punteros existe un operador, &, que indica 'direcci�n de'. Con �l se puede asignar a un puntero la direcci�n de una variable, o pasar como par�metro a una funci�n.
void prueba_puntero ( void ) { long edad; long *p; p = &edad; edad = 50; printf("La edad es %ld\n", edad ); *p = *p / 2; printf("La edad es %ld\n", edad ); } void imprimir_string ( char string[] ) { char *p; for ( p = string; *p != '\0'; p++ ) imprimir_char(*p); }
Definimos un vector de N_PLA componentes de tipo PLANETA. Este tipo est� formado por un registro. Vemos que en la funci�n de inicializaci�n del vector el puntero a la primera componente se inicializa con el nombre del vector. Esto es una caracter�stica importante de C. La direcci�n de la primera componente de un vector se puede direccionar con el nombre del vector. Esto es debido a que en la memoria del ordenador, los distintos elementos est�n ordenados de forma ascendente. As�, SSolar se puede utilizar como &SSolar[0]. A cada iteraci�n llamamos a una funci�n que nos inicializar� los datos de cada planeta. A esta funci�n le pasamos como argumento el puntero a la componente en curso para que, utilizando la notaci�n ->, pueda asignar los valores adecuados a cada campo del registro. Debemos fijarnos en el incremento del puntero de control de la iteraci�n, p++. Con los punteros se pueden realizar determinadas operaciones aritm�ticas aunque, a parte del incremento y decremento, no son muy frecuentes. Cuando incrementamos un puntero el compilador le suma la cantidad necesaria para que apunte al siguiente elemento de la memoria. Debemos fijarnos que esto es aplicable s�lo siempre que haya distintas variables o elementos situados consecutivamente en la memoria, como ocurre con los vectores.
De forma similar se pueden utilizar funciones que tengan como par�metros punteros, para cambiar el valor de una variable. Veamos:
void intercambio ( void ) { int a, b; a = 1; b = 2; swap( &a, &b ); printf(" a = %d b = %d\n", a, b ); } void swap ( int *x, int *y ) { int tmp; tmp = *x; *x = *y; *y = tmp; }
La sintaxis de C puede, a veces, provocar confusi�n. Se debe distinguir lo que es un prototipo de una funci�n de lo que es una declaraci�n de una variable. As� mismo, un puntero a un vector de punteros, etc...
- int f1(); funci�n que devuelve un entero
- int *p1; puntero a entero
- int *f2(); funci�n que devuelve un puntero a entero
- int (*pf)(int); puntero a funci�n que toma y devuelve un entero
- int (*pf2)(int *pi); puntero a funci�n que toma un puntero a entero y devuelve un entero
- int a[3]; vector de tres enteros
- int *ap[3]; vector de tres punteros a entero
- int *(ap[3]); vector de tres punteros a entero
- int (*pa)[3]; puntero a vector de tres enteros
- int (*apf[5])(int *pi); vector de 5 punteros a funci�n que toman un puntero a entero y devuelven un entero.
En los programas que se escriban se debe intentar evitar declaraciones complejas que dificulten la legibilidad del programa. Una forma de conseguirlo es utilizando typedef para redefinir/renombrar tipos.
typedef int *intptr; typedef intptr (*fptr) ( intptr ); fptr f1, f2;