¿Cómo declaro una matriz cuando no conozco la longitud hasta el tiempo de ejecución?
-
10-07-2019 - |
Pregunta
Originalmente tenía una matriz [1..1000] que se definió como una variable global. Pero ahora necesito que sea n, no 1000 y no descubro n hasta más tarde. Sé lo que es n antes de llenar la matriz, pero necesito que sea global, por lo tanto, necesito una forma de definir el tamaño de una matriz global en tiempo de ejecución.
El contexto está llenando una matriz con una transformación lineal de los bytes en un archivo. No sé qué tan grande es el archivo hasta que alguien quiera abrirlo y los archivos pueden ser de cualquier tamaño.
Solución
A partir de Delphi 4, Delphi admite matrices dinámicas . Puede modificar sus tamaños en tiempo de ejecución y retendrán los datos que almacenó en otros elementos en el tamaño anterior. Pueden contener elementos de cualquier tipo homogéneo, incluidos registros y otras matrices. Puede declarar una matriz dinámica de la misma manera que declara normal, "estática". matrices, pero simplemente omita los límites de la matriz:
var
ArthurArray: array of TForm;
Aunque las matrices estáticas le permiten especificar tanto el límite inferior como el superior, el índice bajo de una matriz dinámica siempre es cero. El índice alto viene dado por la función High
, que siempre devuelve uno menos que la longitud de la matriz. Para cualquier matriz dinámica x
, High (x) = Length (x) -1
.
Se puede acceder a una variable global mediante cualquier código, incluidos los procedimientos locales.
Una variable global de tipo de matriz dinámica se inicializará para ser una matriz vacía . Su longitud será cero y High
invocado en esa matriz será -1. Low
en esa matriz aún devolverá cero.
En cualquier momento, puede cambiar el tamaño de una matriz dinámica. Use la función SetLength
, tal como puede hacer con las cadenas:
var
NumElements: Integer;
begin
NumElements := GetNumberOfArthurForms();
SetLength(ArthurArray, NumElements);
end;
Si tiene una matriz multidimensional, puede establecer sus longitudes en un bucle:
var
matrix: array of array of Double;
i: Integer;
begin
SetLength(matrix, height);
for i := 0 to height - 1 do
SetLength(matrix[i], width);
end;
Hay un atajo para eso para establecer las longitudes de todas las matrices internas a la vez:
begin
SetLength(matrix, height, width);
end;
Como mencioné, las matrices dinámicas mantienen sus valores antiguos cuando los redimensiona:
var
data: array of string;
begin
SetLength(data, 2);
data[1] := 'foo';
SetLength(data, 20);
Assert(data[1] = 'foo');
end;
Pero si acorta la matriz, los elementos que residían más allá del nuevo último elemento desaparecerán para siempre:
begin
SetLength(data, 20);
data[15] := 'foo';
SetLength(data, 2);
// data[15] does not exist anymore.
SetLength(data, 16);
writeln(data[15); // Should print an *empty* line.
end;
Mis demostraciones anteriores utilizaron cadenas. Las cuerdas son especiales en Delphi; son administrados por el compilador a través de recuentos de referencias. Debido a eso, los nuevos elementos de matriz dinámica de tipo cadena se inicializan para estar vacíos. Pero si hubiera usado enteros en su lugar, no habría garantía de los valores de los nuevos elementos. Pueden ser cero, pero también pueden ser cualquier otra cosa, al igual que los valores iniciales de variables locales independientes.
Me han dicho que los archivos de ayuda de Delphi 7 son muy buenos. Lea más sobre las matrices dinámicas allí. Puede encontrar demostraciones de su uso en todo el código fuente VCL y RTL proporcionado en su instalación de Delphi, así como en casi cualquier ejemplo de código de Delphi producido en los últimos 10 años.
Otros consejos
Primero, aquí hay una respuesta general a la primera parte de su pregunta:
Si su matriz ya no es estática, puede considerar usar un TList, un TStringList o una de las muchas clases de contenedor en la unidad Contnrs.
Pueden representar mejor lo que está haciendo, proporcionar capacidades adicionales que pueda necesitar, p. clasificación o pares de nombre / valor, crecen dinámicamente a medida que los necesita, y se han optimizado muy bien.
Entonces dijiste:
" El contexto está llenando una matriz con una transformación lineal de los bytes en un archivo. No sé qué tan grande es el archivo hasta que alguien quiera abrirlo y los archivos pueden ser de cualquier tamaño.
Para su problema específico, cargaría los bytes en un archivo usando:
MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite); Size := MyFileStream.Size - MyFileStream.Position; SetLength(Buffer, Size); MyFileStream.Read(Buffer[0], Size);
Entonces puede usar fácilmente un puntero PChar para recorrer cada carácter o incluso cada byte en el Buffer uno por uno y transformarlos de la manera que lo necesite.