【问题标题】:GCSE level delphiGCSE水平德尔福
【发布时间】:2015-10-10 23:56:08
【问题描述】:
var
RegNo: array of string;
Speed: array of real;
cars : integer;
time : real;
begin
Setlength(RegNo, 99);
Setlength(Speed, 99);
Writeln ('The speed limit is 50km/h');
Writeln ('The distance between the two points is 50m');
cars := 0;
time := 1;
while time>0
  do
    begin
    cars := cars + 1;
    Writeln ('Enter the car takes to pass the two points');
    Readln (time);
      if time = 0 then
      Writeln
      else
        if time < 1 then
          begin
           Writeln ('Enter the registration plate for the car');
           Readln (RegNo[cars]);
          Speed[cars]:= 50/time;
          end
        else
    end;
Setlength (RegNo, cars);
Setlength (Speed, cars);
Writeln (RegNo[cars]);
Writeln (Speed[cars]:5:2);
Readln;
end.

我不明白我做错了什么。这应该是一个程序来读取使用数组加速的汽车。它表示变量RegNo 可能尚未初始化。它表示变量Speed 可能尚未初始化。

【问题讨论】:

  • 动态数组使用从零开始的索引。您阅读超出数组末尾的内容。启用范围检查以帮助发现错误。

标签: arrays delphi delphi-7


【解决方案1】:

这是因为您第一次在条件 if 子句中访问这些变量。

因此,如果不满足该条件子句预期的特定条件,则该代码将永远不会运行。

这就是编译器产生上述警告的原因。

但是您的代码中存在更大的问题。由于您使用的是动态数组,因此您需要使用 SetLength(YourArray, NewSize) 设置它们的大小,然后才能将任何数据存储到它们。

如果您在项目中启用了范围检查,否则会引发 EOutOfRange 异常。

如果没有启用范围检查(默认情况下),您可以轻松覆盖属于其他对象或变量的部分内存,从而对您的程序造成严重破坏。

【讨论】:

  • 我已经添加了 Setlength,但我仍然收到错误访问冲突我将如何更改它以访问条件 if 子句之外的变量
  • 现在什么时候出现访问冲突错误。也许当您尝试输入最后一辆车(第 100 辆)的信息时?您会看到动态数组是zero based,这意味着数组中的第一项的索引为0。但是您的代码将有关第一辆车的信息写入数组中的第二项。当您尝试将数据写入最后一项时,您实际上是在尝试访问超出数组范围的内存。所以我建议你启用范围检查。
  • 您是否也考虑过拥有一个或多个记录,其中每个记录将包含有关每辆车的所有必要信息(速度、车牌)。
【解决方案2】:

我不确定你从哪里想到在显然是初学者的编程任务中使用动态数组,因为必须处理内存分配和从零开始的数组索引的复杂性有点分散任务的注意力你在。

Delphi 实现动态数组的主要原因之一是为了处理直到运行时才知道元素数量的情况(另一个是允许编写与特定数量的数组无关的例程元素)。但是,在您的情况下,您不需要使用动态数组的复杂性,它们只是引入了可避免错误的可能性。

在像这样的传统练习中,我们会使用静态数组:

var
  RegNo: array[1..100] of string;  // assuming you actually want 100 elements
  Speed: array[1..100] of real;

这样做的好处包括

  • 数组可以有直观的上下界,而不是 0 和 98:当然,这是假设您确实打算在数组中包含 99 个元素,而不是 100 个,这说明了这种错误很容易如果您使用动态数组或从零开始的数组,则通常在它们不适合您的编程任务时使用。

  • 如果数组是全局变量(不是过程或函数的局部变量),您可以直接使用数组,即无需在使用它们之前对其进行内存分配 (SetLength),也无需之后需要释放内存。原因是在运行时,全局变量会在程序启动时自动填充零,因此程序以已知状态启动它们。

我同意 David Heffernan 的评论,即启用范围检查可以帮助获取超出范围的数组索引,但就这么简单的事情而言,最好不要依赖“训练轮”,而是习惯首先让您的数组索引正确。必须在你的数组心智模型和数组边界和从零开始的数组的索引之间来回切换可能是最常见的错误来源之一。除非有人通过坚持零基数数组故意让您的生活变得困难,否则请使用 Pascal 允许的自然数组边界让您的生活更轻松。

局部变量是在堆栈上分配的(除了某些例外,托管生命周期变量是一个高级主题),因此包含随机值,因此它们应该在使用前进行初始化,方法如下:

FillChar(MyArray, SizeOf(MyArray), Chr(0));

有些人可能不同意,但 imo,最好养成初始化变量的习惯,即使你不需要初始化变量(比如全局变量),而不是绊倒未初始化的变量。

值得一提的是,数组的边界不必是明显的数字。例如,给定一个枚举

type

  TFruit = (apple, pear, banana);

然后你可以声明一个数组

var
  Calories : array[apple..banana] of real;

这是您在 Pascal 教程中经常看到的一种风格,它早于 Delphi,主要归功于它作为一种强类型的教学语言的目标。

【讨论】:

  • 代码中没有任何内容表明 100 就足够了。为什么用户不会输入超过 100 个项目?至于FillChar,这对托管类型来说是个糟糕的建议。
  • @DavidHeffernan: a) 在我看来,OP 想要(或被告知要使用)一个固定大小的数组(也许只是为了习惯数组的概念),而不是支持一些关于“我们今天的阵列有多大?”的突发奇想。 b) 看到我建议 OP 不需要托管类型,我看不出 FillChar 有什么问题,特别是因为它是 OP 可能在传统教程文本中找到的技术,例如TP3 手册及其同类。
  • 学习基于 1 的索引的问题在于,基于 0 的索引将在某处下线,然后会出现严重的混乱。可悲的是,Delphi 很久以前就用基于 1 的虚假字符串索引搞砸了。
  • @MartynA 我同意 David 关于学习使用基于 1 的数组。
猜你喜欢
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 2011-10-04
  • 2012-12-13
  • 1970-01-01
相关资源
最近更新 更多