Процедура Pascal передает массивы переменных

Когда я пытаюсь передать массивы переменных из процедуры в основную программу, a[1] в процедуре должно было быть равно arr[1] в основной программе, например:

a[1] = arr[1]

a[2] = arr[2]

a[3] = arr[3]

a[4] = arr[4]

a[5] = arr[5]

Но на самом деле программа действует так:

a[1] = ''

a[2] = arr[1]

a[3] = arr[2]

a[4] = arr[3]

a[5] = arr[4]

Я не знаю, что не так с кодом, может кто-нибудь указать на ошибку?

Упрощенный код, та же проблема:

var
  arr : array[1..5] of string;
  i : integer;

procedure test(var a : array of string);
var
  i : integer;
begin
  a[1] := 'one';
  a[2] := 'two';
  a[3] := 'three';
  a[4] := 'four';
  a[5] := 'five';
  for i := 1 to 5 do writeln(a[i]);
end;

begin
  test(arr);
  write('-----');
  for i := 1 to 5 do
  begin
    writeln(arr[i]);
    if arr[i] = '' then writeln('NOTHING');
  end;
  readln
end.

person cssjs50    schedule 18.07.2016    source источник
comment
Массив строк, как в вашем тесте, основан на нуле, тогда как ваш arr основан на 1 - массив [1..5] строки;   -  person MartynA    schedule 18.07.2016
comment
Могу ли я объявить массив в тестовой процедуре как основанный на 1?   -  person cssjs50    schedule 18.07.2016
comment
a: массив [1..5] строки не работает   -  person cssjs50    schedule 18.07.2016
comment
FWIW, пожалуйста, прочитайте эту мою статью: Параметры открытого массива и массив констант . Это объясняет это и немного больше о параметрах открытого массива, которые вы используете и с которыми у вас возникают проблемы.   -  person Rudy Velthuis    schedule 19.07.2016
comment
@ cssjs50: как объясняется в моей статье, тип объявление на месте внутри объявления параметра невозможен. Вам нужно будет объявить тип в предложении type и использовать его. Пример: type TArr = array[1..5] of string; ... procedure Test(var a: TArr);.   -  person Rudy Velthuis    schedule 19.07.2016


Ответы (3)


MartynA подсказал вам посмотреть «параметры открытого массива» в онлайн-справке. Но не обязательно делать то, что он предлагает, используя ArrayLoBound и т. д. Объявление реального массива может иметь любой диапазон индексов.

я бы сделал так:

program OpenArrayTest;

{$APPTYPE CONSOLE}

var
  { Initialization like this only possible for global variables. }
  Arr: array[11..15] of string = ('once', 'doce', 'trece', 'catorce', 'quince');
  I: Integer;

procedure ModifyArray(var A: array of string);
var
  I: Integer;
begin
  for I := Low(A) to High(A) do
    A[I] := A[I] + ' <-- ' + IntToStr(I);
end;

procedure ShowArray(const A: array of string);
begin
  for I := Low(A) to High(A) do
    Writeln(A[I]);
end;

begin
  ModifyArray(Arr);
  ShowArray(Arr);
  Writeln('-----');
  ShowArray(['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
  Readln;
end.

Результат:

once <-- 0
doce <-- 1
trece <-- 2
catorce <-- 3
quince <-- 4
-----
one
two
three
four
five
six
seven

Другими словами, используйте High() и Low() для доступа к элементам в параметре. Не используйте фиксированные границы, так как массив может иметь любую длину. Также обратите внимание, что вы можете получить количество элементов в массиве, используя Length(A) или Length(Arr). Вы можете не только передавать статические массивы, такие как Arr, вы также можете передавать динамические массивы или использовать конструктор открытого массива (используя [ и ]), как я сделал во втором вызове ShowArray().

Подробнее об открытых массивах в моей статье "Открытые массивы и массивы const".

person Rudy Velthuis    schedule 18.07.2016
comment
Смысл моего использования констант заключался в том, чтобы ОП мог экспериментировать с их изменением, вот и все. Не важно ... - person MartynA; 19.07.2016

Судя по вашему комментарию, вы немного запутались.

Короче говоря, если вы объявляете параметр процедуры как «массив», массив всегда начинается с нуля, независимо от структуры массива, который вы передаете ему в качестве аргумента, как в

  test(arr);

Попробуйте код ниже. Вы обнаружите, что когда он запускается, вы получаете ошибку Range-Check в строке

  a[5] := 'five';

Это потому, что хотя arr имеет пять элементов, они пронумерованы от 0 до 4, поэтому в arr нет элемента с индексом 5.

Хотя есть и другие способы объявления параметров процедуры, если вы хотите передать ей массивы в качестве аргументов, вы должны убедиться, что либо вы мысленно переводите индексы массива при написании кода, либо (лучше) объявляете массивы, которым вы передаете это как с нулевой базой, как я сделал.

И попробуйте завести привычку включать проверку диапазона. Он поймает ошибку, которую вы сами можете не заметить.

Я оставлю вас переписать вашу test процедуру так, чтобы она работала правильно в качестве упражнения, потому что я "предполагаю", что то, что вы разместили, является своего рода школьной или курсовой работой, и вам действительно следует добавить некоторые усилия, чтобы выяснить, как исправить свою ошибку самостоятельно. Если вы все еще застряли после прочтения этого и попытки найти очевидное решение, спросите.

Кстати, если вы используете Delphi, найдите «Параметры открытого массива» в интерактивной справке. Это объясняет ограничения на использование "массива ..." параметров процедуры.

Также, кстати, Руди Велтус говорит в своем ответе: «Но нет необходимости делать то, что предлагает {MartynA], используя ArrayLoBound и т. Д.». Это правда, в этом нет необходимости, но он упустил мою мысль. Если вы жестко запрограммируете границы массива со значениями, такими как 1 и 5, а затем измените их позже, легко упустить из виду другие значения, которые также нуждаются в обновлении, как в вашем цикле for. Определять эти значения как consts — хорошая привычка, потому что это позволяет избежать несоответствий, но, что более важно, заставляет вас думать о том, что вы делаете. ИМЭ...

program arrayparam;

const
  ArrayLoBound = 0;
  ArrayHiBound = 4;

var
  arr : array[ArrayLoBound..ArrayHiBound] of string;
  i : integer;

{$R+}  // Turn range-checking on

procedure test(var a : array of string);
var
  i : integer;
begin
  a[1] := 'one';
  a[2] := 'two';
  a[3] := 'three';
  a[4] := 'four';
  a[5] := 'five';
  for i := 1 to 5 do
    writeln(a[i]);
end;

begin
  test(arr);
  writeln('-----');

  for  i := ArrayLoBound to ArrayHiBound do
  begin
    writeln(arr[i]);
    if arr[i] = '' then
      writeln('NOTHING');
  end;
  readln
end.
person MartynA    schedule 18.07.2016
comment
На самом деле передаваемый массив не обязательно должен начинаться с нуля. Он просто индексируется с нулевой базой внутри подпрограммы, имеющей параметр открытого массива. Другими словами: formal параметр открытого массива отсчитывается от нуля. Переданный массив фактический не обязательно должен быть таким. - person Rudy Velthuis; 19.07.2016
comment
Другими словами, вы можете передать array[11..16] параметру открытого массива. Но внутри он будет доступен как 0..5. Так что нет необходимости в ваших ArrayLoBound и т. д. Лучше научите их использовать Low() и High(). Это намного безопаснее. - person Rudy Velthuis; 19.07.2016

Все хорошие ответы, но только для полноты: спрашивающий может получить именно тот результат, который запросил.

Возможно, доступ к ним с использованием цифр от 1 до 5 важен для целей спрашивающего.

Внесите изменения ниже, и он будет распечатан так, как ожидалось изначально.

тип

TArr = массив [ 1..5 ] строки;

вар

обр: тарр;

процедура test( var a : TArr );

Я согласен с тем, что по умолчанию использовать массивы на основе 0 просто проще, а использование функций low/hi делает его пуленепробиваемым.

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

person juus    schedule 19.07.2016