加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 处理复杂记录的指针

发布时间:2020-12-15 09:41:59 所属栏目:大数据 来源:网络整理
导读:我有一些指向一些复杂记录的指针.有时当我尝试处理它们时,我得到无效的指针操作错误.我不确定我是否正确地创建和处理它们. 记录看起来像这样: type PFILEDATA = ^TFILEDATA; TFILEDATA = record Description80: TFileType80; // that's array[0..80] of Wid
我有一些指向一些复杂记录的指针.有时当我尝试处理它们时,我得到无效的指针操作错误.我不确定我是否正确地创建和处理它们.
记录看起来像这样:

type
  PFILEDATA = ^TFILEDATA;
  TFILEDATA = record
    Description80: TFileType80;  // that's array[0..80] of WideChar
    pFullPath: PVeryLongPath;    // this is pointer to array of WideChar
    pNext: PFILEDATA;            // this is pointer to the next TFILEDATA record
  end;

据我所知,当我想要一个指向这样的记录的指针时,我需要初始化指针和动态数组,如下所示:

function GimmeNewData(): PFILEDATA;
begin
  New(Result);
  New(Result^.pFullPath);
end;

现在处理这些记录的系列我写道:

procedure DisposeData(var pData: PFILEDATA);
var pNextData: PFILEDATA;
begin
  while pData^.pNext <> nil do begin
    pNextData := pData^.pNext;          // Save pointer to the next record
    Finalize(pData^.pFullPath);         // Free dynamic array
    Dispose(pData);                     // Free the record
    pData := pNextData;
  end;
  Finalize(pData^.pFullPath);
  Dispose(pData);
  pData := nil;
end;

当我在Delphi 2010 IDE中以调试模式(F9)运行我的程序时,会发生一些奇怪的事情.当我通过F8步进DisposeData代码时,似乎程序跳过Finalize(pData ^ .pFullPath)行并跳转到Dispose(pData).这是正常的吗?此外,当执行Dispose(pData)时,显示指针内容的“局部变量”窗口不会更改.这是否意味着处置失败?

编辑:
PVeryLongPath是:

type
  TVeryLongPath = array of WideChar;
  PVeryLongPath = ^TVeryLongPath;

EDIT2

所以我创建2个TFILEDATA记录然后我处理它们.然后我再次创建相同的2条记录.由于某种原因,第二次记录中的这个时间pNext不是零.它指向第一记录.处理这个奇怪的事情会导致无效的指针操作错误.
我随机地在DisposeData过程中插入了pData ^ .pNext:= nil.
现在代码看起来像这样:

procedure DisposeData(var pData: PFILEDATA);
var pNextData: PFILEDATA;
begin
  while pData <> nil do begin
    pNextData := pData^.pNext;
    pData^.pNext := nil;          // <----
    Dispose(pData^.pFullPath);
    Dispose(pData);
    pData := pNextData;
  end;
end;

错误消失了.
我将尝试将PVeryLongPath更改为TVeryLongPath.

解决方法

您接受Serg的答案这一事实表明您的节点创建代码存在问题.您对该答案的评论证实了这一点.

我将此添加为新答案,因为对该问题的编辑会显着改变它.

链接列表代码应如下所示:

var
  Head: PNode=nil;
  //this may be a global variable,or better,a field in a class,//in which case it would be initialized to nil on creation

function AddNode(var Head: PNode): PNode;
begin
  New(Result);
  Result.Next := Head;
  Head := Result;
end;

请注意,我们将节点添加到列表的头部.我们不需要在任何地方初始化Next到nil,因为我们总是将另一个节点指针指向Next.这条规则很重要.

我把它写成一个返回新节点的函数.由于新节点始终添加在头部,因此这有点多余.因为你可以忽略函数返回值,所以它并没有真正造成任何伤害.

有时,您可能希望在添加新节点时初始化节点的内容.例如:

function AddNode(var Head: PNode; const Caption: string): PNode;
begin
  New(Result);
  Result.Caption := Caption;
  Result.Next := Head;
  Head := Result;
end;

我更喜欢这种方法.始终确保您的字段已初始化.如果零初始化对您没问题,那么您可以使用AllocMem来创建节点.

这是使用这种方法的更具体的例子:

type
  PNode = ^TNode;
  TNode = record
    Caption: string;
    Next: PNode;
  end;

procedure PopulateList(Items: TStrings);
var
  Item: string;
begin
  for Item in Items do
    AddNode(Head,Item);
end;

要销毁列表,代码运行如下:

procedure DestroyList(var Head: PNode);
var
  Next: PNode;
begin
  while Assigned(Head) do begin
    Next := Head.Next;
    Dispose(Head);
    Head := Next;
  end;
end;

您可以清楚地看到此方法只能在Head为零时返回.

如果将链接列表封装在类中,则可以将头指针作为类的成员,并避免传递它.

我想说的主要观点是手动内存分配代码很精细.在细节上很容易犯错误.在这样的情况下,将精巧的代码放在辅助函数或方法中是值得的,因此您只需要编写一次.链接列表是一个很好的例子,可以解决泛型问题.您可以编写一次内存管理代码,并将其重用于各种不同的节点类型.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读