Borland Delphi: 我该如何调试这个程序?
Borland Delphi: How do I debug this program?
我必须使用 VCL 形式在 Delphi 中编写程序。正方形、六角形、八角形三个图形必须移动到上边框,然后到下边框,依此类推。问题是我的程序冻结了,当我试图将值放入条件运算符以停止移动时,如果坐标 Y = 0。虽然它有效(奇怪地),例如,如果我将值 = 180。
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
Image: TImage;
BeginButton: TButton;
EndButton: TButton;
Timer1: TTimer;
Edit1: TEdit;
procedure FormActivate(Sender: TObject);
procedure BeginButtonClick(Sender: TObject);
procedure EndButtonClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses Figure;
{$R *.dfm}
Var
t:single=0.0;
L:TSquare;
S:THexagon;
C:TOctagon;
Moving:Boolean=true;
procedure TMainForm.FormActivate(Sender: TObject);
begin
Image.Canvas.Brush.Color:=clWhite;
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
L.Move(t);
S.Move(-0.2*t);
C.Move(0.5*t);
t:=t+0.5;
end;
procedure TMainForm.BeginButtonClick(Sender: TObject);
begin
L:=TSquare.Create(60,35,Image);
S:=THexagon.Create(180,100,Image);
C:=TOctagon.Create(300,100,Image);
Timer1.Enabled:=true;
end;
procedure TMainForm.EndButtonClick(Sender: TObject);
begin
Close;
end;
initialization
finalization
L.Free;
S.Free;
C.Free;
end.
第二个单位:
Unit Figure;
Interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
Type
TFigure=Class
private x,y, b,
dx:integer;
Image:TImage;
procedure Draw;virtual;abstract;
procedure Rel(t:real);virtual;
public
constructor Create(ax,ay:integer;aImage:TImage);
procedure Move(t:single);
end;
THexagon=Class(TFigure)
private procedure Draw;override;
end;
TSquare=Class(TFigure)
private procedure Draw;override;
end;
TOctagon=Class(TFigure)
private procedure Draw;override;
end;
Implementation
Constructor TFigure.Create;
Begin
inherited Create;
x:=ax; y:=ay; Image:=aImage;
End;
Procedure TFigure.Rel;
Begin
dx:=5*round(t);
End;
Procedure TFigure.Move;
Begin
Image.Canvas.Pen.Color:=clWhite;
Draw;
Image.Canvas.Pen.Color:=clBlack;
Rel(t);
Draw;
End;
Procedure TSquare.Draw;
Begin
b:=70;
Image.Canvas.MoveTo(x+round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y-round(0.5*b));
End;
Procedure THexagon.Draw;
Begin
b:=70;
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
End;
Procedure TOctagon.Draw;
var
I: Integer;
p: array[1..9] of tpoint;
u:extended;
Begin
x:=300;
y:=100;
u:=0;
for I := 1 to 8 do
begin
p[i].X:=x+round(40*cos(u));
p[i].Y:=y-round(40*sin(u));
u:=u+pi/4;
end;
repeat
begin
Image.Canvas.MoveTo(p[8].x,p[8].y-dx);
for I := 1 to 8 do
Image.Canvas.LineTo(p[i].X,p[i].y-dx);
end;
until (p[3].y>50);
End;
end.
Delphi 带有集成调试器。你应该使用它。以下是如何开始调查程序似乎挂起的情况。
- 使用 "play" 按钮在调试器的控制下启动程序。
- 重现您正在尝试调查的情况。
- 当程序挂起时,切换到调试器并按下"pause"按钮。调试器将中断程序的执行,以便您可以调查当前状态。
查看调用堆栈。 (如果调用堆栈 window 尚不可见,您可以使用 IDE 中的 "debug windows" 菜单选项显示它。)
调用堆栈将显示您的程序调用的函数列表。在堆栈的顶部将是您的程序在您暂停时 运行ning 的函数。它下面的函数将是调用当前函数的函数,依此类推,直到到达堆栈底部,它代表程序的主要函数。
您停止的函数可能不是您编写的函数。相反,它通常是由 OS 或 Delphi 运行-time 库提供的函数。你不想调试那些。通常,您可以假设它们已经正常工作。您正在寻找 您的 代码中的错误。
使用"run until return"命令让最顶层的函数继续运行ning。重复该操作,直到您到达调用堆栈中的某个函数。这可能是罪魁祸首。
现在您已经确定了有问题的函数,是时候进一步调查它了。
- 使用 "step over" 调试器命令逐行 运行 函数的每一行。 (还有一个 "step into" 命令,但它会进入不属于你的函数,你现在对那些不感兴趣。)
观察代码中变量的当前值。您可以将鼠标悬停在一个变量上,让调试器在工具提示中显示它的值,或者您可以使用 "watches" 调试 window 一次显示多个变量。它们将在您的程序中的每个步骤后更新。
注意变量的值。你应该已经对他们的价值观在你的项目过程中应该如何发展有了一些期望。您可能在编写代码时考虑过这种进展。回想那个时候,将你在调试器中观察到的结果与你之前的预期进行比较。他们匹配吗?如果是这样,则继续单步执行代码。但是,如果它们不匹配,那么您就发现了一个错误。修复它。
意外行为的另一个来源是在您的程序中达到了您不希望达到的程度。也许程序调用了一个它不应该调用的函数,或者也许你已经执行了多次你想要的循环。如果你能找出原因,然后修复错误。否则,您可能需要备份一些方法。
在您的程序中发现一个比您观察到意外行为更早的点。在代码编辑器的左边距中查找蓝点。这些点代表您可以设置 断点 的地方。单击其中一个点,您应该会注意到该行被突出显示(可能是红色)。
终止您的程序,然后 运行 再次执行。
这一次,您应该看到调试器在程序出现挂起之前停止,因为执行将首先到达断点。调试器会在那里中断您的程序。
像以前一样单步执行代码行,并观察导致程序偏离预期执行路径的情况。确定错误后,修复它。
它卡住了,因为你的 repeat-until 循环永远不会结束。
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
它的条件基于 y、b 和 dx 值,但它们永远不会改变在你的循环中。
要确认挂起的位置,请在Delphi中使用暂停命令,然后逐步按F7/F8到运行它。
我必须使用 VCL 形式在 Delphi 中编写程序。正方形、六角形、八角形三个图形必须移动到上边框,然后到下边框,依此类推。问题是我的程序冻结了,当我试图将值放入条件运算符以停止移动时,如果坐标 Y = 0。虽然它有效(奇怪地),例如,如果我将值 = 180。
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
Image: TImage;
BeginButton: TButton;
EndButton: TButton;
Timer1: TTimer;
Edit1: TEdit;
procedure FormActivate(Sender: TObject);
procedure BeginButtonClick(Sender: TObject);
procedure EndButtonClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses Figure;
{$R *.dfm}
Var
t:single=0.0;
L:TSquare;
S:THexagon;
C:TOctagon;
Moving:Boolean=true;
procedure TMainForm.FormActivate(Sender: TObject);
begin
Image.Canvas.Brush.Color:=clWhite;
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
L.Move(t);
S.Move(-0.2*t);
C.Move(0.5*t);
t:=t+0.5;
end;
procedure TMainForm.BeginButtonClick(Sender: TObject);
begin
L:=TSquare.Create(60,35,Image);
S:=THexagon.Create(180,100,Image);
C:=TOctagon.Create(300,100,Image);
Timer1.Enabled:=true;
end;
procedure TMainForm.EndButtonClick(Sender: TObject);
begin
Close;
end;
initialization
finalization
L.Free;
S.Free;
C.Free;
end.
第二个单位:
Unit Figure;
Interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
Type
TFigure=Class
private x,y, b,
dx:integer;
Image:TImage;
procedure Draw;virtual;abstract;
procedure Rel(t:real);virtual;
public
constructor Create(ax,ay:integer;aImage:TImage);
procedure Move(t:single);
end;
THexagon=Class(TFigure)
private procedure Draw;override;
end;
TSquare=Class(TFigure)
private procedure Draw;override;
end;
TOctagon=Class(TFigure)
private procedure Draw;override;
end;
Implementation
Constructor TFigure.Create;
Begin
inherited Create;
x:=ax; y:=ay; Image:=aImage;
End;
Procedure TFigure.Rel;
Begin
dx:=5*round(t);
End;
Procedure TFigure.Move;
Begin
Image.Canvas.Pen.Color:=clWhite;
Draw;
Image.Canvas.Pen.Color:=clBlack;
Rel(t);
Draw;
End;
Procedure TSquare.Draw;
Begin
b:=70;
Image.Canvas.MoveTo(x+round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y-round(0.5*b));
Image.Canvas.LineTo(x-round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y+round(0.5*b));
Image.Canvas.LineTo(x+round(0.5*b),y-round(0.5*b));
End;
Procedure THexagon.Draw;
Begin
b:=70;
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
End;
Procedure TOctagon.Draw;
var
I: Integer;
p: array[1..9] of tpoint;
u:extended;
Begin
x:=300;
y:=100;
u:=0;
for I := 1 to 8 do
begin
p[i].X:=x+round(40*cos(u));
p[i].Y:=y-round(40*sin(u));
u:=u+pi/4;
end;
repeat
begin
Image.Canvas.MoveTo(p[8].x,p[8].y-dx);
for I := 1 to 8 do
Image.Canvas.LineTo(p[i].X,p[i].y-dx);
end;
until (p[3].y>50);
End;
end.
Delphi 带有集成调试器。你应该使用它。以下是如何开始调查程序似乎挂起的情况。
- 使用 "play" 按钮在调试器的控制下启动程序。
- 重现您正在尝试调查的情况。
- 当程序挂起时,切换到调试器并按下"pause"按钮。调试器将中断程序的执行,以便您可以调查当前状态。
查看调用堆栈。 (如果调用堆栈 window 尚不可见,您可以使用 IDE 中的 "debug windows" 菜单选项显示它。)
调用堆栈将显示您的程序调用的函数列表。在堆栈的顶部将是您的程序在您暂停时 运行ning 的函数。它下面的函数将是调用当前函数的函数,依此类推,直到到达堆栈底部,它代表程序的主要函数。
您停止的函数可能不是您编写的函数。相反,它通常是由 OS 或 Delphi 运行-time 库提供的函数。你不想调试那些。通常,您可以假设它们已经正常工作。您正在寻找 您的 代码中的错误。
使用"run until return"命令让最顶层的函数继续运行ning。重复该操作,直到您到达调用堆栈中的某个函数。这可能是罪魁祸首。
现在您已经确定了有问题的函数,是时候进一步调查它了。
- 使用 "step over" 调试器命令逐行 运行 函数的每一行。 (还有一个 "step into" 命令,但它会进入不属于你的函数,你现在对那些不感兴趣。)
观察代码中变量的当前值。您可以将鼠标悬停在一个变量上,让调试器在工具提示中显示它的值,或者您可以使用 "watches" 调试 window 一次显示多个变量。它们将在您的程序中的每个步骤后更新。
注意变量的值。你应该已经对他们的价值观在你的项目过程中应该如何发展有了一些期望。您可能在编写代码时考虑过这种进展。回想那个时候,将你在调试器中观察到的结果与你之前的预期进行比较。他们匹配吗?如果是这样,则继续单步执行代码。但是,如果它们不匹配,那么您就发现了一个错误。修复它。
意外行为的另一个来源是在您的程序中达到了您不希望达到的程度。也许程序调用了一个它不应该调用的函数,或者也许你已经执行了多次你想要的循环。如果你能找出原因,然后修复错误。否则,您可能需要备份一些方法。
在您的程序中发现一个比您观察到意外行为更早的点。在代码编辑器的左边距中查找蓝点。这些点代表您可以设置 断点 的地方。单击其中一个点,您应该会注意到该行被突出显示(可能是红色)。
终止您的程序,然后 运行 再次执行。
这一次,您应该看到调试器在程序出现挂起之前停止,因为执行将首先到达断点。调试器会在那里中断您的程序。
像以前一样单步执行代码行,并观察导致程序偏离预期执行路径的情况。确定错误后,修复它。
它卡住了,因为你的 repeat-until 循环永远不会结束。
repeat
begin
Image.Canvas.MoveTo(x+round(0.5*b),y+dx);
Image.Canvas.LineTo(x+round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.25*b),y+round(0.5*b)+dx);
Image.Canvas.LineTo(x-round(0.5*b),y+dx);
Image.Canvas.LineTo(x-round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.25*b),y-round(0.5*b)+dx);
Image.Canvas.LineTo(x+round(0.5*b),y+dx);
end;
until ((y+round(0.5*b)+dx)<180);
它的条件基于 y、b 和 dx 值,但它们永远不会改变在你的循环中。
要确认挂起的位置,请在Delphi中使用暂停命令,然后逐步按F7/F8到运行它。