Pascal过程传递变量数组

4
当我试图从过程传递变量数组到主程序时,过程中的 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.

你的“Test”中的“字符串数组”是从零开始的,而你的arr是从1开始的 - “array [1..5] of string;”。 - MartynA
我可以在测试过程中将数组声明为基于1的吗? - cssjs50
a: 字符串数组[1..5]无法工作。 - cssjs50
顺便提一下,请阅读我写的这篇文章:"Open array parameters and array of const"。它解释了关于开放数组参数的更多内容,就像你正在使用并且遇到问题的那样。 - Rudy Velthuis
正如我的文章所解释的那样,在参数声明中无法直接进行类型声明。您需要在type子句中声明一个类型并使用它。例如:type TArr = array[1..5] of string; ... procedure Test(var a: TArr); - Rudy Velthuis
3个回答

2

MartynA给了你提示,让你在在线帮助中查看“open array parameters”。但是他提出的使用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()的第二次调用中所做的那样。
更多关于开放数组的信息,请参见我的文章“开放数组和数组常量”

我使用常量的目的是让OP可以尝试更改它们,仅此而已。无论如何... - MartynA

1
根据您的评论,您有些困惑。
简而言之,如果您在过程中声明一个参数为“数组”,则无论传递给它作为参数的数组结构如何,该数组始终是从零开始的,例如:
  test(arr);

尝试以下代码。运行时,您会发现在该行出现范围检查错误。
  a[5] := 'five';

那是因为尽管“arr”有五个元素,但它们的编号是0..4,所以没有具有索引5的“arr”元素。
虽然有其他方法来声明过程参数,但如果要将数组作为参数传递给它,必须确保要么在编写代码时在心理上将数组索引进行转换,要么(更好的方法)将传递给它的数组声明为基于零的,就像我所做的那样。
而且,试着养成打开范围检查的习惯。这将捕捉您自己可能忽略的错误。
我将让您重写您的“test”过程,以便作为练习正确地工作,因为我“猜测”您发布的内容是某种学校或课程作业,您应该真正努力找出如何自行纠正错误。如果在阅读此内容并尝试显而易见的解决方案后仍然卡住,请询问。
顺便说一句,如果您使用Delphi,请在在线帮助中查找“Open array parameters”。这解释了在使用“array of ...”过程参数时的限制。
此外,Rudy Velthuis在他的回答中说:“但是没有必要像[MartynA]建议的那样使用ArrayLoBound等。” 这是真的,这并不是必要的,但他忽略了我的观点。如果您硬编码数组边界,使用像1和5这样的值,然后稍后更改它们,很容易忽略其他需要更新的值,例如您的for循环中的值。将这些值定义为const是一个好习惯,因为它避免引入不一致性,但更重要的是确保您考虑清楚自己在做什么。根据我的经验...
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.

实际上,传入的数组不一定要从零开始。它只是在具有开放数组参数的例程中以零为基础进行索引。换句话说:形式上,开放数组参数是以零为基础的。实际上传入的数组不必如此。 - Rudy Velthuis
换句话说,您可以将 array[11..16] 传递给开放数组参数。但是在其中,它将被视为 0..5 进行访问。因此,您不需要使用 ArrayLoBound 等方法。相反,教他们使用 Low()High() 更安全。 - Rudy Velthuis

0

所有的答案都很好,但为了完整起见:提问者可以得到所要求的确切结果。

对于提问者的目的来说,使用1到5访问它们可能很重要。

进行以下更改,它将按最初预期的方式打印出来。

类型

TArr = array[1..5] of string;

变量

arr: TArr;

过程测试(var a: TArr);

我同意默认为基于0的数组只是更简单,使用函数low / hi使其防弹。

但我也能看到有时以自己的方式进行索引可能会很有用/重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接