Logo

Marco Cantù
Pascal Esencial

Capítulo 8 Actualizar
Memoria

Nota del autor: En un futuro, este capítulo cubrirá el manejo de la memoria, discutirá varias áreas de memoria, e introducirá vectores dinámicos. Por el momento, sólo esta última parte está disponible.


Vectores dinámicos en Delphi 4

Traditionalmente, en el lenguaje Pascal siempre se usaron vectores de tamaño fijo. Cuando se declara un tipo de dato usando el formato vectorial (array), se tiene que especificar el número de elementos del vector. Como saben los programadores expertos, existen unas pocas técnicas que pueden utilizarse para implementar vectores dinámicos, normalmente usando punteros y adjudicando y liberando manualmente la memoria requerida.

Delphi 4 introduce una implementación muy simple de los vectores dinámicos, modelándolos mediante el tipo dinámico de cadena larga que expuse anteriormente. Como las cadenas largas, los vectores dinámicos se adjudican de forma dinámica, e incluyen un contador de referencias, aunque no implementan la técnica de copia por escritura (copy-on-write). Esto no supone gran problema, ya que se puede vaciar un vector declarando su variable nula (nil).

Ahora, puede declarar un vector de forma simple, sin tener que especificar el número de elementos y luego adjudicarlos con un tamaño dado, utilizando el procedimiento SetLength. El mismo procedimiento nos servirá para cambiar el tamaño de un vector sin perder su contenido. Hay otros procedimientos referidos a las cadenas, como la función Copy, que pueden utilizarse sobre los vectores.

He aquí una pequeña muestra de código, sin mostrar la necesidad de declarar y adjudicar memoria al vector antes de empezar a usarlo:

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1: array of Integer;
begin
  Array1 [1] := 100; // error
  SetLength (Array1, 100);
  Array1 [99] := 100; // OK
  ...
end;

Si sólo se indica el número de elementos del vector, el índice siempre comienza a contar desde el 0. Los vectores genéricos en Pascal pueden tener cota inferior no nula e índices no enteros. Este no es el caso en los vectores dinámicos. Para averiguar la condición de un vector dinámico, puede usar las funciones Length, High y Low, como con cualquier vector. Sin embargo, en el caso de vectores dinámicos, Low siempre devuelve el valor 0, y High el valor menos uno. Esto implica que para un vector vacío High vale -1 (que, si lo pensamos, es un valor extraño, ya que es inferior al dado por Low).

Figura 8.1: El formulario del ejemplo DynArr

Tras esta breve introducción puedo mostrarle un sencillo ejemplo, llamado DynArr, que se muestra en la Figura 8.1. De hecho, es sencillo, ya que los vectores dinámicos no tienen nada de complejo. También lo utilizaré para mostrar algunos errores en que un programador puede incurrir. El programa declara dos vectores globales e inicializa el primero en el gestor de eventos OnCreate.

var
  Array1, Array2: array of Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // adjudicar
  SetLength (Array1, 100);
end;

Esto iguala todos los valores a cero. Este código de inicialización hace posible comenzar a leer y escribir valores del vector sin más, sin temor a errores de memoria. (Asumiendo, por supuesto, que no intente acceder a nada que esté por encima del vector.) Para una inicialización aún mejor, el programa tiene un botón que escribe en cada celda del vector:

procedure TForm1.btnFillClick(Sender: TObject);
var
  I: Integer;
begin
  for I := Low (Array1) to High (Array1) do
    Array1 [I] := I;
end;

El botón Grow permite modificar el tamaño del vector sin perder su contenido. Puede probar esto usando el botón Get value tras pulsar el botón Grow:

procedure TForm1.btnGrowClick(Sender: TObject);
begin
  // grow mantiene los valores existentes
  SetLength (Array1, 200);
end;

procedure TForm1.btnGetClick(Sender: TObject);
begin
  // extraer
  Caption := IntToStr (Array1 [99]);
end;

La única parte del código algo compleja corresponde al evento OnClick del botón Alias. El programa copia un vector al otro con el operador := , en realidad creando un alias (una nueva variable que apunta al mismo vector en memoria). Aquí, sin embargo, si modifica uno de los vectores, el otro también se ve afectado, ya que ambos se refieren a la misma área de memoria:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  // alias
  Array2 := Array1;
  // cambiar uno (cambian ambos)
  Array2 [99] := 1000;
  // mostrar el otro
  Caption := IntToStr (Array1 [99]);

El método btnAliasClick realiza una o dos operaciones. La primera es comprobar la igualdad de los vectores. Esto no comprueba los elementos que de hecho forman las estructuras, sino las áreas de memoria a que apuntan los vectores, comprobando si las variables son dos alias del mismo vector en memoria:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  ...
  if Array1 = Array2 then
    Beep;
  // truncar el primer vector
  Array1 := Copy (Array2, 0, 10);
end;

El segundo es una llamada a la función Copy, que no sólo mueve datos de un vector al otro, sino que también reemplaza el primer vector con uno nuevo, creado por la función. El efecto es que la variable Array1 ahora apunta a un vector de 11 elementos, de tal forma que pulsar los botones Get value o Set value produce un error de memoria y muestra una excepción (a no ser que haya desactivado la comprobación de rango, en cuyo caso el error se produce igualmente, pero la excepción no se muestra). El código del botón Fill sigue funcionando perfectamente tras este cambio, ya que qué miembros del vector se han de modificar viene determinado por sus cotas actuales.

Conclusión

Por el momento, este capítulo cubre solamente variables dinámicas, que son un recurso importante para administrar la memoria, pero sólo son una parte del todo. Añadiré más material en el futuro.

La estructura de memoria descrita en este capítulo es típica de la programación en Windows, un tema que introduciré en el siguiente capítulo (sin extenderme al uso del VCL).

Capítulo siguiente: Programación bajo Windows

© Copyright Marco Cantù, Wintech Italia Srl 1995-99
© Copyright de la traducción, Rafael Barranco-Droege, 2000