Kromster
Kromster

Reputation: 7387

How to track down double pointer usage mistakes

Recently I've ported a huge chunk of code form C++ to Delphi (over 1mb of C code). The code is full with pointers. Despite project compiling and working well for 99% of the time, I'm seeing some weird results from time to time, which leads me to think there might be bugs with pointer handling - indeed, I've already found a couple. The problem is that they are really hard to track down without any clues/hints from compiler.

Maybe you have some tips, for such cases:

var
  a: PSingle;

GetMem(a, SizeOf(Single));

a^ := 1.1;

// Call func declared as - someFunc(aIn: PSingle);

someFunc(@a); // <<-- Bug here. I mistakely added @ while porting. 
              // someFunc needs PSingle, but gets a PPSingle instead

In the example, there's erroneous @ inserted. Program does not crash, just deals with that erroneous data and keeps running. I need a way of finding such cases where "Pointer to a Pointer to a Value" gets passed instead of "Pointer to a Value".

How do you track down pointer bugs like these?

Upvotes: 1

Views: 213

Answers (2)

Rob Kennedy
Rob Kennedy

Reputation: 163247

The "typed @ operator" compiler option is designed to detect exactly that kind of mistake. When it's disabled, the expression @a has type Pointer, which is compatible with all pointer types. Enable the option, and the same expression has type ^PSingle, which is not compatible with the expected PSingle.

I recommend turning on that option in all projects; it baffles me that Delphi doesn't make that option the default all the time.

You can modify the state of that option within your code with the $T+ and $T- compiler directives.


Another thing you can do is convert your code to use more idiomatic Delphi. For example, pass the argument by reference instead of by pointer value. Change the definition of the argument:

procedure someFunc(var arg: Single);

With that declaration, passing @a will be an error that the compiler will find and forbid. You would instead pass a^, or you could even get rid of the pointer entirely and just declare a as a plain Single rather than PSingle.

Just because the original code was written in C and C-style C++, it doesn't mean your Delphi code has to look like it.

Upvotes: 12

Jens Borrisholt
Jens Borrisholt

Reputation: 6402

The problem is here:

  someFunc(@a);

You are adding a pointer to a pointer not the pointer it self.

Let me make you an example:

uses Math;

function Somefunc(value: pSingle): Single; begin Result := (value^ + 1.1) end;

procedure TForm23.FormCreate(Sender: TObject);
var
  a: pSingle;
  b: Single;
begin
  GetMem(a, SizeOf(Single));
  a^ := 1.1;

  //Example 1:
  b := Somefunc(a);
  Caption := BoolToStr(CompareValue(2.2, b, 0.00001) = 0., True); //Caption: True

  //Example 2:
  b := Somefunc(@a); //NOTE @a
  Caption := BoolToStr(CompareValue(1.1, b, 0.00001) = 0., True); //Caption: True
  FreeMem(a);
end;

As you see in the first example the actual value are changed, while in example 2 the value og a remains unchanged because you parse the pointer to the pointer and therefor it is the pointer you chanhe and not the value.

Upvotes: 0

Related Questions