Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disposing pointers to complex records

I have list of pointers to some complex records. Sometimes when I try disposing them I get invalid pointer operation error. I'm not really sure if I'm creating and disposing them properly. The record looks like this:

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;

As I understand when I want a pointer to such record I need to initialize the pointer and the dynamic arrays like this:

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

Now to dispose of series of these records I wrote this:

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;

When I run my program in the debug mode (F9) in the Delphi 2010 IDE something weird happens. When I step trough DisposeData code with F8 it appears that program skips Finalize(pData^.pFullPath) line and jumps to Dispose(pData). Is this normal? Also when Dispose(pData) is executed the Local variables window that displays contents of the pointers does not change. Does this mean that dispose fails?

Edit: PVeryLongPath is:

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

Edit2

So I create 2 TFILEDATA records then I dispose them. Then I create the same 2 records again. For some reason this time pNext in the second record is not nil. It points to the 1st record. Disposing this weird thing gets invalid pointer operation error. Randomly I have inserted pData^.pNext := nil in the DisposeData procedure. Now the code looks like this:

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;

The error is gone. I'll try to change PVeryLongPath into TVeryLongPath.

like image 444
Antans Avatar asked Dec 07 '22 20:12

Antans


1 Answers

First, if you free something, the contents of pointers to it do not change. That is why you don't see a change in the local variables display.

EDIT: declare pFullPath as TVeryLongPath. This is a reference type already, and you should not use a pointer to such a type. New() doesn't do what you think it does, in such a case.

It would probably be better if you declared it as UnicodeString, or if your Delphi doesn't have that, WideString.

If pFullPath is declared as a dynamic "array of WideChar", then you should not use New() on it. For dynamic arrays, use SetLength() and nothing else. Dispose() will properly dispose of all items in your record, so just do:

New(Result);
SetLength(Result^.pFullPath, size_you_need);

and later:

Dispose(pData);

In normal code, you should never have to call Finalize(). This is all taken care of by Dispose, as long as you pass a pointer of the correct type to Dispose().

FWIW, I would recommend this and this article of mine.

like image 84
Rudy Velthuis Avatar answered Dec 28 '22 02:12

Rudy Velthuis