Curso de C++ (Página 13a)

pagina012b Principal pagina013b

CAPITULO 13a Tipos de variables IV: Punteros 1

Los punteros proporcionan la mayor parte de la potencia al C y C++, y marcan la principal diferencia con otros lenguajes de programación.

Una buena comprensión y un buen dominio de los punteros pondrá en tus manos una herramienta de gran potencia. Un conocimiento mediocre o incompleto te impedirá desarrollar programas eficaces.

Por eso le dedicaremos mucha atención y mucho espacio a los punteros. Es muy importante comprender bien cómo funcionan y cómo se usan.

Para entender qué es un puntero veremos primero cómo se almacenan los datos en un ordenador.

La memoria de un ordenador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, y por eso se agrupan.

Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes. Y aquí llegamos al quid, cada byte tiene una dirección, llamada normalmente dirección de memoria.

La unidad de información básica es la palabra, dependiendo del tipo de microprocesador una palabra puede estar compuesta por dos, cuatro, ocho o dieciséis bytes. Hablaremos en estos casos de plataformas de 16, 32, 64 ó 128 bits. Se habla indistintamente de direcciones de memoria, aunque las palabras sean de distinta longitud. Cada dirección de memoria contiene siempre un byte. Lo que sucederá cuando las palabras sean de 32 bits es que accederemos a posiciones de memoria que serán múltiplos de 4.

Todo esto sucede en el interior de la máquina, y nos importa más bien poco. Podemos saber qué tipo de plataforma estamos usando averiguando el tamaño del tipo int, y para ello hay que usar el operador "sizeof()", por ejemplo:

cout << "Plataforma de " << 8 * sizeof(int) << " bits" << endl; 

Ahora veremos cómo funcionan los punteros. Un puntero es un tipo especial de variable que contiene, ni más ni menos que, una dirección de memoria. Por supuesto, a partir de esa dirección de memoria puede haber cualquier tipo de objeto: un char, un int, un float, un array, una estructura, una función u otro puntero. Seremos nosotros los responsables de decidir ese contenido.

Declaración de punteros:

Los punteros se declaran igual que el resto de las variables, pero precediendo el identificador con el operador de indirección, (*), que leeremos como "puntero a".

Sintaxis:

<tipo> *<identificador>;

Ejemplos:

int *entero;
char *carácter;
struct stPunto *punto;

Los punteros siempre apuntan a un objeto de un tipo determinado, en el ejemplo, "entero" siempre apuntará a un objeto de tipo "int".

La forma:

<tipo>* <indentificador>;

con el (*) junto al tipo, en lugar de junto al identificador de variable, también está permitida.

Veamos algunos matices. Tomemos el primer ejemplo:

int *entero;

equivale a:

int* entero;

Debes tener muy claro que "entero" es una variable del tipo "puntero a int", y que "*entero" NO es una variable de tipo "int".

Si "entero" apunta a una variable de tipo "int", "*entero" será el contenido de esa variable, pero no olvides que "*entero" es un operador aplicado a una variable de tipo "puntero a int", es decir "*entero" es una expresión, no una variable.

Para averiguar la dirección de memoria de cualquier variable usaremos el operador de dirección (&), que leeremos como "dirección de".

Declarar un puntero no creará un objeto. Por ejemplo: "int *entero;" no crea un objeto de tipo "int" en memoria, sólo crea una variable que puede contener una dirección de memoria. Se puede decir que existe físicamente la variable "entero", y también que esta variable puede contener la dirección de un objeto de tipo "int". Lo veremos mejor con otro ejemplo:

int A, B; 
int *entero; 
... 
B = 213; /* B vale 213 */ 
entero = &A; /* entero apunta a la dirección de la variable A */ 
*entero = 103; /* equivale a la línea A = 103; */ 
B = *entero; /* equivale a B = A; */ 
...

En este ejemplo vemos que "entero" puede apuntar a cualquier variable de tipo "int", y que podemos hacer referencia al contenido de dichas variables usando el operador de indirección (*).

Como todas las variables, los punteros también contienen "basura" cuando son declaradas. Es costumbre dar valores iniciales nulos a los punteros que no apuntan a ningún sitio concreto:

entero = NULL; 
caracter = NULL; 

NULL es una constante, que está definida como cero en varios ficheros de cabecera, como "stdio.h" o "iostream.h", y normalmente vale 0L.


pagina012b Principal pagina013b