Getting allocation address of an object in Delphi 7
Asked Answered
S

2

5

I have the following code sequence:

program OverrideAfterConstructionEtc;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type

TA = class( TInterfacedObject)
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
protected
FDummyData: array[ 1 .. 1000 ] of longint;
end;

{ TA }

procedure TA.AfterConstruction;
var
    selfPtr: Pointer;
    selfInt: Integer;
    selfStr: string;
    size: Integer;
begin
    inherited AfterConstruction;
    selfPtr := Addr( self );
    selfInt := Integer( selfPtr );
    selfStr := IntToHex( selfInt, 8 );

    size := TA.InstanceSize;
    WriteLn( 'TA instance allocated at 0x', selfStr );
    WriteLn( 'TA size is ', size );


end;

procedure TA.BeforeDestruction;
var
    selfPtr: Pointer;
    selfInt: Integer;
    selfStr: string;

    size: Integer;

begin

    selfPtr := Addr( self );
    selfInt := Integer( selfPtr );
    selfStr := IntToHex( selfInt, 8 );

    WriteLn( 'Preparing to destroy TA instance allocated at 0x', selfStr );

    size := TA.InstanceSize;
    WriteLn( 'TA size is ', size );

    inherited BeforeDestruction;
end;

const
    maxDummy = 1000;
var
    a: TA;
    dummy: TList;
    iter : integer;

    dummies: array [ 1 .. maxDummy ] of TList;
begin

    // Simulate a set of allocations.

    for iter := 1 to maxDummy do
    begin
        dummy := TList.Create;
        dummies[ iter ] := dummy;
    end;

    // Allocate the object we want to track.
    a := TA.Create;

    // Release the simulated allocations.
    for iter := 1 to maxDummy do
    begin
        dummy := dummies[ iter ];
        dummies[ iter ] := nil;
        FreeAndNil( dummy );
    end;



    // Release the tracked object.

    FreeAndNil( a );

end.

The output of the code is:

  • TA instance allocated at 0x0012FF88
  • TA size is 4012 Preparing to destroy
  • TA instance allocated at 0x0012FF80
  • TA size is 4012

I do not understand the "self" difference. Can you give me a hint? I would have expected the printed values to be the same.

Snitch answered 12/1, 2011 at 15:35 Comment(0)
A
15

Self in an instance method is an implicit argument and is a reference to the instance that received the method call. It is implemented as a pointer.

The Addr standard procedure is the same as the @ operator; it takes the address of the location passed to it. When you apply Addr to Self, you are getting the address of the parameter location, not the address of the instance. This parameter location is allocated on the stack, as it only needs to exist while the method call is active; when the method call returns, there is no longer any need for the space for the Self parameter; it gets released, implicitly, by the CPU's stack pointer moving back to whatever it was before the method was called.

The reason why the Self parameter may be at different locations in different invocations of methods on the same instance is because there may be more or less data on the stack at the time of the call. For example, the Self parameter will be allocated differently if the method call sequence looks like A -> B -> C versus A -> C, because the stack frame for B needs to be stored in between the stack frames for A and C. The stack frame is where the parameters and locals associated with any given invocation of a method are allocated.

Ammonic answered 12/1, 2011 at 17:3 Comment(0)
L
13

The following code will give you an explanation:

type
  TA = class( TInterfacedObject)
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

procedure TA.AfterConstruction;
begin
  inherited;
  writeln('AfterConstruction=',integer(self));
  writeln('AfterConstruction=',integer(addr(self)));
end;

procedure TA.BeforeDestruction;
begin
  writeln('BeforeDestruction=',integer(self));
  writeln('BeforeDestruction=',integer(addr(self)));
  inherited;
end;

Here is the output:

AfterConstruction=10731904
AfterConstruction=1245020
BeforeDestruction=10731904
BeforeDestruction=1245028

So integer(self) is correct (both values equal 10731904), but integer(addr(self)) is not.

Because addr(self) don't show the self value, but where the self value is stored.

In both case, it's stored in the stack (use Alt-F2 to disassemble the prefix of both methods):

mov [esp],eax

So when you use addr(self), you're looking at the current stack address, not the self value.

Addr is not a simple typecast to a pointer but the same as @self, so using integer(self) is not the same as integer(addr(self)).

You should not use Addr(self) but self to find the allocation address of your object.

Laconic answered 12/1, 2011 at 17:2 Comment(2)
Thank you for your answers. How do I mark both of them as answers (the web UI only allows me to select one of them)?Snitch
@Snitch I think you can only select one! That's the deal! :)Laconic

© 2022 - 2024 — McMap. All rights reserved.