delphi – 处理复杂记录的指针
我有一些指向一些复杂记录的指针.有时当我尝试处理它们时,我得到无效的指针操作错误.我不确定我是否正确地创建和处理它们.
记录看起来像这样: 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)时,显示指针内容的“局部变量”窗口不会更改.这是否意味着处置失败? 编辑: type TVeryLongPath = array of WideChar; PVeryLongPath = ^TVeryLongPath; EDIT2 所以我创建2个TFILEDATA记录然后我处理它们.然后我再次创建相同的2条记录.由于某种原因,第二次记录中的这个时间pNext不是零.它指向第一记录.处理这个奇怪的事情会导致无效的指针操作错误. 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; 错误消失了. 解决方法
您接受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为零时返回. 如果将链接列表封装在类中,则可以将头指针作为类的成员,并避免传递它. 我想说的主要观点是手动内存分配代码很精细.在细节上很容易犯错误.在这样的情况下,将精巧的代码放在辅助函数或方法中是值得的,因此您只需要编写一次.链接列表是一个很好的例子,可以解决泛型问题.您可以编写一次内存管理代码,并将其重用于各种不同的节点类型. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |