Как мне объявить массив, если я не знаю длину до времени выполнения?
-
10-07-2019 - |
Вопрос
У меня изначально был массив [1..1000], который был определен как глобальная переменная. Но теперь мне нужно, чтобы это было n, а не 1000, и я не узнаю до позже. Я знаю, что такое n, прежде чем заполнять массив, но мне нужно, чтобы он был глобальным, поэтому мне нужен способ определить размер глобального массива во время выполнения.
Контекст - это заполнение массива линейным преобразованием байтов в файле. Я не знаю, насколько большой файл, пока кто-то не захочет открыть его, и файлы могут быть любого размера.
Решение
Начиная с Delphi 4, Delphi поддерживает динамические массивы . Вы можете изменить их размеры во время выполнения, и они сохранят данные, которые вы сохранили в других элементах, в старом размере. Они могут содержать элементы любого однородного типа, включая записи и другие массивы. Вы можете объявить динамический массив так же, как и обычный, " статический " массивы, но просто опустите границы массива:
var
ArthurArray: array of TForm;
Хотя статические массивы позволяют указывать как нижнюю, так и верхнюю границу, нижний индекс динамического массива всегда равен нулю. Высокий индекс задается функцией High
, которая всегда возвращает на единицу меньше длины массива. Для любого динамического массива x
, High (x) = Length (x) -1
.
К глобальной переменной может обращаться любой код, включая локальные процедуры.
Глобальная переменная типа динамического массива будет инициализирована как массив пустой . Его длина будет равна нулю, а High
для этого массива будет равно -1. Low
в этом массиве все равно вернет ноль.
В любой момент вы можете изменить размер динамического массива. Используйте функцию SetLength
, как вы можете это сделать со строками:
var
NumElements: Integer;
begin
NumElements := GetNumberOfArthurForms();
SetLength(ArthurArray, NumElements);
end;
Если у вас есть многомерный массив, вы можете установить их длину в цикле:
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;
Для этого есть ярлык для одновременной установки длин всех внутренних массивов:
begin
SetLength(matrix, height, width);
end;
Как я уже говорил, динамические массивы сохраняют свои старые значения при изменении их размера:
var
data: array of string;
begin
SetLength(data, 2);
data[1] := 'foo';
SetLength(data, 20);
Assert(data[1] = 'foo');
end;
Но если вы укоротите массив, все элементы, находящиеся за пределами нового последнего элемента, исчезнут навсегда:
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;
Мои демонстрации выше использовали строки. Строки являются особенными в Delphi; они управляются компилятором через счетчик ссылок. Из-за этого новые элементы динамического массива типа string инициализируются как пустые. Но если бы я использовал вместо этого целые числа, не было бы никакой гарантии значений новых элементов. Они могут быть нулевыми, но они могут быть и другими, как и начальные значения автономных локальных переменных.
Файлы справки Delphi 7 очень хороши, как мне сказали. Пожалуйста, прочитайте больше о динамических массивах там. Демонстрации их использования можно найти в исходном коде VCL и RTL, представленном в вашей установке Delphi, а также практически в любом примере кода Delphi, созданном за последние 10 лет.
Другие советы
Во-первых, вот общий ответ на первую часть вашего вопроса:
Если ваш массив больше не статичен, вы можете рассмотреть возможность использования TList, TStringList или одного из множества контейнерных классов в модуле Contnrs.
Они могут лучше представлять, что вы делаете, предоставлять дополнительные возможности, которые вам могут понадобиться, например, сортировка или пары имя / значение, они динамически растут по мере необходимости и были очень хорошо оптимизированы.
<Ч>Тогда вы сказали:
" Контекст заполняет массив с помощью линейного преобразования байтов в файле. Я не знаю, насколько большой файл, пока кто-то не захочет его открыть, и файлы могут быть любого размера. & Quot;
Для вашей конкретной задачи я бы загрузил байты в файл, используя:
MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite); Size := MyFileStream.Size - MyFileStream.Position; SetLength(Buffer, Size); MyFileStream.Read(Buffer[0], Size);
Затем вы можете легко использовать указатель PChar, чтобы последовательно проходить каждый символ или даже каждый байт в буфере и преобразовывать их так, как вам нужно.