在 Delphi 7 中获取对象的分配地址

Posted

技术标签:

【中文标题】在 Delphi 7 中获取对象的分配地址【英文标题】:Getting allocation address of an object in Delphi 7 【发布时间】:2011-01-12 15:35:15 【问题描述】:

我有以下代码序列:

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.

代码的输出是:

TA 实例分配在 0x0012FF88 TA 大小为 4012 准备销毁 TA 实例分配在 0x0012FF80 TA 大小为 4012

我不明白“自我”的区别。你能给我一个提示吗?我本来希望打印的值是相同的。

【问题讨论】:

【参考方案1】: 实例方法中的

Self 是一个隐式参数,是对接收方法调用的实例的引用。它被实现为一个指针。

Addr 标准过程与@ 操作符相同;它需要传递给它的位置的地址。当你将Addr 应用到Self 时,你得到的是参数位置的地址,不是实例的地址。该参数位置在堆栈上分配,因为它只需要在方法调用处于活动状态时存在;当方法调用返回时,不再需要Self 参数的空间;它通过 CPU 的堆栈指针返回到调用该方法之前的位置隐式释放。

Self参数之所以可能在同一个实例的不同方法调用中位于不同的位置,是因为调用时栈上可能有或多或少的数据。例如,如果方法调用序列类似于A -> B -> CA -> C,则Self 参数的分配方式将不同,因为B 的堆栈帧需要存储在A 和@ 的堆栈帧之间987654333@。堆栈帧是分配与任何给定方法调用相关的参数和局部变量的位置。

【讨论】:

【参考方案2】:

下面的代码会给你一个解释:

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;

这是输出:

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

所以 integer(self) 是正确的(两个值都等于 10731904),但是 integer(addr(self)) 不是。

因为 addr(self) 不显示 self 值,而是显示 self 值的存储位置。

在这两种情况下,它都存储在堆栈中(使用 Alt-F2 反汇编两种方法的前缀):

mov [esp],eax

因此,当您使用 addr(self) 时,您查看的是当前堆栈地址,而不是 self 值。

Addr 不是指针的简单类型转换而是与@self 相同,因此使用 integer(self) 与 integer(addr(self)) 不同。

您不应该使用 Addr(self) 而是使用 self 来查找对象的分配地址。

【讨论】:

感谢您的回答。如何将它们都标记为答案(Web UI 只允许我选择其中一个)? @Dan 我想你只能选择一个!就是这样! :)

以上是关于在 Delphi 7 中获取对象的分配地址的主要内容,如果未能解决你的问题,请参考以下文章

Delphi 7中快速获取本机IP地址

如何使用在设计时获取“无法将 NIL 分配给 TFont”的 TFont 属性修复 Delphi 组件?

在delphi中如何获取字符串的内存地址

delphi 获取本地IP地址的几种方法

如何获取 Delphi 程序使用的内存

Delphi 7,使用FastMM4获取应用路径并打开应用