C (lenguaje de programación), 8ª parte, Tipos de datos derivados.

Además de los tipos de datos fundamentales vistos, en C existen algunos otros tipos de datos muy utilizados y que se pueden considerar derivados de los anteriores. En esta sección se van a presentar los punteros, las matrices y las estructuras.


Punteros.

Concepto de puntero o apuntador.

El valor de cada variable está almacenado en un lugar determinado de la memoria, caracterizado por una dirección (que se suele expresar con un número hexadecimal). El ordenador mantiene una tabla de direcciones que relaciona el nombre de cada variable con su dirección en la memoria. Gracias a los nombres de las variables (identificadores), de ordinario no hace falta que el programador se preocupe de la dirección de memoria donde están almacenados sus datos. Sin embargo, en ciertas ocasiones es más útil trabajar con las direcciones que con los propios nombres de las variables. El lenguaje C dispone del operador dirección (&) que permite determinar la dirección de una variable, y de un tipo especial de variables destinadas a contener direcciones de variables. Estas variables se llaman punteros o apuntadores (en inglés pointers).

Así pues, un puntero es una variable que puede contener la dirección de otra variable. Por supuesto, los punteros están almacenados en algún lugar de la memoria y tienen su propia dirección (más adelante se verá que existen punteros a punteros). Se dice que un puntero apunta a una variable si su contenido es la dirección de esa variable. Un puntero ocupa de ordinario 4 bytes de memoria, y se debe declarar o definir de acuerdo con el tipo del dato al que apunta. Por ejemplo, un puntero a una variable de tipo int se declara del siguiente modo:

int *direc;

lo cual quiere decir que a partir de este momento, la variable direc podrá contener la dirección de cualquier variable entera. La regla nemotécnica es que el valor al que apunta direc (es decir *direc, como luego se verá), es de tipo int. Los punteros a long, char, float y double se definen análogamente a los punteros a int.

Operadores dirección (&) e indirección (*).

Como se ha dicho, el lenguaje C dispone del operador dirección (&) que permite hallar la dirección de la variable a la que se aplica. Un puntero es una verdadera variable, y por tanto puede cambiar de valor, es decir, puede cambiar la variable a la que apunta. Para acceder al valor depositado en la zona de memoria a la que apunta un puntero se debe utilizar el operador indirección (*). Por ejemplo, supóngase las siguientes declaraciones y sentencias,

int i, j, *p; // p es un puntero a int
p = &i; // p contiene la dirección de i
*p = 10; // i toma el valor 10
p = &j; // p contiene ahora la dirección de j
*p = -2; // j toma el valor -2

Las constantes y las expresiones no tienen dirección, por lo que no se les puede aplicar el operador (&). Tampoco puede cambiarse la dirección de una variable. Los valores posibles para un puntero son las direcciones posibles de memoria. Un puntero puede tener valor 0 (equivalente a la constante simbólica predefinida NULL). No se puede asignar una dirección absoluta directamente (habría que hacer un casting). Las siguientes sentencias son ilegales:

p = &34; // las constantes no tienen dirección
p = &(i+1); // las expresiones no tienen dirección
&i = p; // las direcciones no se pueden cambiar
p = 17654; // habría que escribir p = (int *)17654;

Para imprimir punteros con la función printf() se deben utilizar los formatos %u y %p, como se verá más adelante.

No se permiten asignaciones directas (sin casting) entre punteros que apuntan a distintos tipos de variables. Sin embargo, existe un tipo indefinido de punteros (void *, o punteros a void), que puede asignarse y al que puede asignarse cualquier tipo de puntero. Por ejemplo:

int *p;
double *q;
void *r;
p = q; // ilegal
p = (int *)q; // legal
p = r = q; // legal

ARITMÉTICA DE PUNTEROS

Como ya se ha visto, los punteros son unas variables un poco especiales, ya que guardan información –no sólo de la dirección a la que apuntan–, sino también del tipo de variable almacenado en esa dirección. Esto implica que no van a estar permitidas las operaciones que no tienen sentido con direcciones de variables, como multiplicar o dividir, pero sí otras como sumar o restar. Además estas operaciones se realizan de un modo correcto, pero que no es el ordinario. Así, la sentencia:

p = p+1;

hace que p apunte a la dirección siguiente de la que apuntaba, teniendo en cuenta el tipo de dato. Por ejemplo, si el valor apuntado por p es short int y ocupa 2 bytes, el sumar 1 a p implica añadir 2 bytes a la dirección que contiene, mientras que si p apunta a un double, sumarle 1 implica añadirle 8 bytes.

También tiene sentido la diferencia de punteros al mismo tipo de variable. El resultado es la distancia entre las direcciones de las variables apuntadas por ellos, no en bytes sino en datos de ese mismo tipo. Las siguientes expresiones tienen pleno sentido en C:

p = p + 1;
p = p + i;
p += 1;
p++;

La dirección de memoria está en hexadecimal, con el segmento y el offset separados por dos puntos (:); basta prestar atención al segundo de estos números, esto es, al offset.

Un Saludo.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s