Delphi指针问题

3
我有以下代码,它是可行的,但我并不完全理解它(请看代码中的注释):
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TMyRec=record
    a:Integer;
    b:String;
  end;
  TRecArray=array of TMyRec;
  PRecArray = ^TRecArray;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
   v1:TRecArray;
   procedure Test(a:PRecArray);
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
 SetLength(v1,3);
 v1[0].b:='test1';//set the first value
 Test(PRecArray(v1));//call method to change the value assigned before
end;

procedure TForm1.Test(a: PRecArray);
begin
 ShowMessage(v1[0].b);//shows test1
 try
  a^[0].b:='test2' //this is raising an error...
 except

 end;
 PRecArray(@a)^[0].b:='test3';//this is working...
 ShowMessage(v1[0].b);//shows test3
end;

end.

我不明白为什么"a^[0].b:='test2'"会引起一个错误。
谢谢!

请将TPointerArrayRec替换为PArrayRec,因为这是声明指针类型的标准约定。 - iamjoosy
1
顺便提一下,按照惯例,应该将PArrayRec = ^TArrayRec命名为TPointerArryRec,而不是。我个人会称它们为TRecArrayPRecArray,即以Array结尾。我看到@iamjoosy几乎在同一时间有同样的想法。 - Rudy Velthuis
好的,但现在我也必须在我的答案中进行更改。;-) - Rudy Velthuis
是的,让我们都改变我们的答案! - Sertac Akyuz
3个回答

10

你的“Test”过程需要一个“PRecArray”,但你正在将一个“TRecArray”传递给它。尝试这样调用:

 Test(@v1);//call method to change the value assigned before
将'TRecArray'强制转换为'PRecArray'并不会使它变成'PRecArray'。(注意:你的'test3'将当然失败。)

+1 是的,你说得对。在问一些“愚蠢”的问题之前,我必须先睡一会儿。 - RBA
1
@RBA - 我不认为这很傻,使用指针可能总是令人困惑。但是睡眠是好的 :) - Sertac Akyuz

4

我看到了几件可疑的事情。

1

几乎不需要将指针指向动态数组,因为动态数组变量已经是指针(好吧, 引用)。
要将这样的数组传递给函数或过程,请使用 var 参数:
procedure TForm1.Test(var a: TRecArray);

现在您无需使用指针语法来访问数组:
a[0].b := 'test2';

2

您使用以下方式调用Test:

Test(PRecArray(v1));

在您的原始代码中,Test使用了PRecArray,但您没有传递它(你传递的是TRecArray),因此您应该这样做:
Test(@v1); // or Test(Addr(v1));

应用我上面的更改,当 Test 有一个 var 参数时,只需使用:
Test(v1);

3

好的,这可能不可疑,但我想推荐我的文章《解决Delphi程序员指针问题》,它讲解了你似乎遇到的许多问题。


+1 Rudy。是的,我知道你的文章,顺便说一下,写得非常好。问题是那段代码是如何工作的。它不是我写的,我也不理解。感谢你的建议。 - RBA
@Marjan:牙科只占据我“上班时间”的一部分。我不打猎也不打高尔夫。<g> 几个月前我买了一台Mac,现在正在努力熟悉Cocoa和Objective-C。这是一门有趣的语言,但没有Delphi。我迫不及待地等待XE2的到来。 - Rudy Velthuis
@Rudy,啊是的,不打猎或高尔夫确实可以节省很多时间 :-)) 我也在等待XE,特别是64位(工作),但跨平台也一样(个人项目)。 - Marjan Venema
我看到了几件可疑的事情 - 你确定第三点是可疑的吗? - Sertac Akyuz
嗯...显然有人不喜欢我宣传我的文章。我在两个答案中提到我的文章以供进一步阅读,结果被踩了。这是其中之一。<g> - Rudy Velthuis
显示剩余4条评论

1
你可以替换掉


procedure TForm1.Test(a: TPointerArrayRec);

使用

   procedure TForm1.Test(var a: TArrayRec);   

这样更简单,而且您不必使用弃用解引用运算符^。


+1,是的我知道,但我想要以实际形式理解代码。 - RBA
我认为“弃用”应该被解引用(或取消引用)... 弃用是将某些东西标记为“弃用”(不再使用并有被删除的危险)。 - Marjan Venema
@Marjan 无论我说了什么,那个符号 ^ 就足够了。 - opc0de
@opc0de:不,不是这样的。Marjan是正确的,你应该编辑你的帖子来纠正你的错误。除非叫你“oopscode”会更接近一些,因为它以“o”开头,以“de”结尾。 - Ken White

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