2.3 Asignación dinámica a estructuras.
Hemos visto ya
cómo las variables son las células de memoria a las que podemos tener acceso
por un identificador. Pero estas variables se guardan en lugares concretos de
la memoria de la computadora. Para nuestros programas, la memoria de la
computadora es solamente una sucesión de las células de 1 octeto (la talla
mínima para un dato), cada una con una dirección única.
La memoria de computadora puede ser comparada con una calle
en una ciudad. En una calle todas las casas se numeran consecutivamente con un
identificador único tal que si hablamos del número 27 de la calle Córdova,
podremos encontrar el lugar sin pérdida, puesto que debe haber solamente una
casa con ese número y, además, nosotros sabemos que la casa estará entre las
casas 26 y 28.
Una declaración de
puntero consiste en un tipo base, un * y el nombre de la variable.
La forma general de
declaración de una variable puntero es:
Tipo *nomb_var;
Donde:
Tipo: cualquier tipo
valido ,ya sea primitivo o definido por el usuario
nomb_var: es el nombre
de la variable de tipo apuntador.
Los operadores de punteros
Existen dos operadores especiales de punteros: & y *.
El & devuelve la dirección de memoria de su operando.
Por ejemplo:
m=&cuenta;
pone en m la dirección de memoria de la variable cuenta.
Esta dirección es la posición interna de la variable en la computadora. La
dirección no tiene nada que ver con el valor de cuenta. Se puede pensar en el
operador & como devolviendo “la dirección de”.
El segundo operador de punteros, *, es el complemento de
&. Devuelve el valor de la variable localizada en la dirección que sigue.
Por ejemplo, si m contiene la dirección de memoria de la variable cuenta,
entonces:
q=*m;
pone el valor de cuenta en q. Se puede pensar en * como “en
la dirección”.
El siguiente programa ilustra un ejemplo:
#include <stdio.h>
main()
{int cuenta, q;
int *m;
cuenta=100;
m=&cuenta; //m
recibe la dirección de cuenta
q=*m; //a q se le
asigna el valor de cuenta
indirectamente a través de m
print(“%d,q”) //imprime
100
}
Punteros estáticos
Definamos un puntero a un entero
y una variable entera como sigue:
Int *p1;
Int valor1;
Con estas definiciones es posible
hacer las siguientes asignaciones estáticas:
p1= *valor1;
*p1=25;
El apuntador p1 se define como un
apuntador a un entero. La variable valor2 se define como una variable entera.
La primera asignación hace que p1 apunte a la variable valor1, la segunda
asignación almacena en memoria el valor 25 en donde p1 está apuntando.
Se dice que este tipo
de inicialización es de tipo estática porque la asignación de la memoria que se
utiliza para almacenar es fija. Una vez definida la variable, el compilador
establece suficiente memoria para almacenar un valor de un tipo dado. Esta
memoria permanece reservada para esta variable y no es posible usarla para nada
más hasta que se termine la función.
La segunda forma para
inicializar un puntero es por medio de la asignación dinámica de memoria. Por
asignación dinámica de la memoria se entiende que se reserva memoria cuando se
necesite para almacenar un valor de un tipo dado. Después, una vez que no se
necesite el valor, es posible liberar la memoria y hacerla disponible para otro
uso por el sistema .
De nuevo definamos a
p1 como un valor entero como sigue:
Int *p1;
Ahora es posible
inicializar a p1 en forma dinámica para apuntar a un valor de la siguiente
manera:
p1=new int;
*p1=25;
Esta vez no es
necesario inicializar primero p1 a la dirección de un a variable estática. En
cambio el operador new crea suficiente memoria para contener un valor entero
apuntado por p1. Después se almacena en
esta área de memoria el valor 25. Una vez que se asigna dinámicamente la
memoria como ésta, es posible liberar la misma área de memoria usando el
operador delete, como sigue:
Delete p1;
Esta operación
liberará la memoria apuntada por p1.
Es importante señalar que el operador delete no elimina el apuntador, simplemente libera el área de memoria al cual se dirige el puntero. Por tanto luego que se ejecute el enunciado anterior, p1 todavía existe como un puntero que no apunta a nada, pero que es posible inicializarlo de nuevo para apuntar a otro entero utilizando el operador new.