首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >处理指向复杂记录的指针

处理指向复杂记录的指针
EN

Stack Overflow用户
提问于 2011-08-06 05:15:48
回答 5查看 3.8K关注 0票数 4

我有一些指向一些复杂记录的指针列表。有时,当我尝试处理它们时,我得到无效的指针操作错误。我真的不确定我是否正确地创建和处理了它们。记录如下所示:

代码语言:javascript
复制
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;

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

代码语言:javascript
复制
function GimmeNewData(): PFILEDATA;
begin
  New(Result);
  New(Result^.pFullPath);
end;

现在,为了处理一系列这些记录,我写了以下内容:

代码语言:javascript
复制
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;

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

编辑: PVeryLongPath为:

代码语言:javascript
复制
type
  TVeryLongPath = array of WideChar;
  PVeryLongPath = ^TVeryLongPath;

Edit2

因此,我创建了两个TFILEDATA记录,然后将它们处理掉。然后,我再次创建相同的两个记录。由于某些原因,这一次第二条记录中的pNext不是nil。它指向第一个记录。处理这个奇怪的东西会得到无效的指针操作错误。我随机地在DisposeData过程中插入了pData^.pNext := nil。现在代码看起来像这样:

代码语言:javascript
复制
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。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-08-06 17:25:35

您接受了Serg的回答,这表明您的节点创建代码有问题。你对这个答案的评论证实了这一点。

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

链表代码应如下所示:

代码语言:javascript
复制
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 anywhere,因为我们总是将另一个节点指针分配给Next。这条规则很重要。

我把它写成一个返回新节点的函数。因为新节点总是添加在头部,所以这有点多余。因为你可以忽略函数的返回值,所以这并没有什么坏处。

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

代码语言:javascript
复制
function AddNode(var Head: PNode; const Caption: string): PNode;
begin
  New(Result);
  Result.Caption := Caption;
  Result.Next := Head;
  Head := Result;
end;

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

下面是一个使用这种方法的更具体的例子:

代码语言:javascript
复制
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;

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

代码语言:javascript
复制
procedure DestroyList(var Head: PNode);
var
  Next: PNode;
begin
  while Assigned(Head) do begin
    Next := Head.Next;
    Dispose(Head);
    Head := Next;
  end;
end;

您可以清楚地看到,只有当Headnil时,此方法才能返回。

如果您将链表封装在一个类中,则可以使头指针成为该类的成员,从而避免了传递它的需要。

我想说的主要一点是,手动内存分配代码是微妙的。很容易在细节上犯一些小错误。在这种情况下,将微妙的代码放在helper函数或方法中是值得的,因此您只需要编写一次。链表是一个很好的例子,它喜欢用泛型来解决问题。您可以只编写一次内存管理代码,然后将其重用于各种不同的节点类型。

票数 4
EN

Stack Overflow用户

发布于 2011-08-06 05:33:16

首先,如果你释放了一些东西,指向它的指针的内容不会改变。这就是为什么在局部变量显示中看不到变化的原因。

编辑:将pFullPath声明为TVeryLongPath。这已经是一个引用类型,您不应该使用指向此类类型的指针。在这种情况下,New()并不像您认为的那样工作。

如果你把它声明为UnicodeString可能会更好,或者如果你的Delphi没有,WideString。

如果pFullPath被声明为一个动态的“WideChar数组”,那么您不应该对它使用New()。对于动态数组,使用SetLength()而不使用其他方法。Dispose()将正确地处理记录中的所有项,所以只需这样做:

代码语言:javascript
复制
New(Result);
SetLength(Result^.pFullPath, size_you_need);

之后的版本:

代码语言:javascript
复制
Dispose(pData);

在普通代码中,您永远不需要调用Finalize()。这一切都由Dispose负责,只要您将正确类型的指针传递给Dispose()即可。

顺便说一句,我推荐我的thisthis文章。

票数 5
EN

Stack Overflow用户

发布于 2011-08-06 05:26:36

我建议您避免使用WideChar的动态数组,因为使用起来一点也不方便。如果您使用的是Delphi2009或更高版本,请使用string,或者对于较早的Delphi版本,请使用WideString。这两种类型都是带有WideChar元素的动态字符串类型。你可以赋值给它们,Delphi会处理所有的分配。

因此,假设您现在拥有以下记录:

代码语言:javascript
复制
TFILEDATA = record
  Description80: TFileType80;
  pFullPath: WideString; 
  pNext: PFILEDATA;          
end;

你可以让事情变得相当简单。

代码语言:javascript
复制
function GimmeNewData(): PFILEDATA;
begin
  New(Result);
end;

procedure DisposeData(var pData: PFILEDATA);
var pNextData: PFILEDATA;
begin
  while pData <> nil do begin
    pNextData := pData^.pNext;          
    Dispose(pData);                     
    pData := pNextData;
  end;
end;
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6962735

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档