Как мне объявить массив, если я не знаю длину до времени выполнения? - PullRequest
33 голосов
/ 11 января 2009

У меня изначально был массив [1..1000], который был определен как глобальная переменная. Но теперь мне нужно, чтобы это было n, а не 1000, и я не узнаю до позже. Я знаю, что такое n, прежде чем заполнять массив, но мне нужно, чтобы он был глобальным, поэтому мне нужен способ определения размера глобального массива во время выполнения.

Контекст заполняет массив с помощью линейного преобразования байтов в файле. Я не знаю, насколько большой файл, пока кто-то не захочет открыть его, и файлы могут быть любого размера.

Ответы [ 2 ]

89 голосов
/ 11 января 2009

Начиная с Delphi 4, Delphi поддерживает динамические массивы . Вы можете изменить их размеры во время выполнения, и они сохранят данные, которые вы сохранили в других элементах, в старом размере. Они могут содержать элементы любого однородного типа, включая записи и другие массивы. Вы можете объявить динамический массив так же, как вы объявляете обычные «статические» массивы, но просто опустите границы массива:

var
  ArthurArray: array of TForm;

Хотя статические массивы позволяют указывать как нижнюю, так и верхнюю границу, нижний индекс динамического массива всегда равен нулю. Высокий индекс задается функцией High, которая всегда возвращает на единицу меньше длины массива. Для любого динамического массива x, High(x) = Length(x)-1.

К глобальной переменной может обращаться любой код, включая локальные процедуры.

Глобальная переменная типа динамического массива будет инициализирована как массив empty . Его длина будет равна нулю, а 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 лет.

1 голос
/ 11 января 2009

Во-первых, вот общий ответ на первую часть вашего вопроса:

Если ваш массив больше не статичен, вы можете рассмотреть возможность использования TList, TStringList или одного из множества контейнерных классов в модуле Contnrs.

Они могут лучше представлять, что вы делаете, предоставлять дополнительные возможности, которые вам могут понадобиться, например, Сортировка или пары имя / значение, они динамически растут по мере необходимости и были очень хорошо оптимизированы.


Тогда вы сказали:

"Контекст заполняет массив с помощью линейного преобразования байтов в файле. Я не знаю, насколько велик этот файл, пока кто-то не захочет открыть его, и файлы могут быть любого размера."

Для вашей конкретной задачи я бы загрузил байты в файл, используя:

  MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  Size := MyFileStream.Size - MyFileStream.Position;
  SetLength(Buffer, Size);
  MyFileStream.Read(Buffer[0], Size);

Затем вы можете легко использовать указатель PChar, чтобы последовательно проходить через каждый символ или даже каждый байт в буфере и преобразовывать их так, как вам нужно.

...