You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi中如何正确实现返回记录指针的get_Record过程?

如何正确实现返回自定义记录指针的Delphi过程

问题背景

你已经成功实现了返回PAnsiChar类型指针的get_String过程:

procedure get_String(var aStringPtr: PAnsiChar);
  function set_ReturnValue(aInputValue: AnsiString) : PAnsiChar; overload;
  var
    iLen: Integer;
  begin
    iLen   := Length(aInputValue);
    Result := AnsiStrAlloc(iLen + 1); // 修正原代码笔误:iStringLen改为iLen
    System.AnsiStrings.StrPLCopy(Result, aInputValue, iLen);
  end;

var
  iData: AnsiString;
begin
  iData := 'Hello World';
  aStringPtr:= set_ReturnValue(iData);
end;

但在编写返回自定义记录指针的get_Record过程时,无法正确为aRecordPtr赋值,所有尝试均触发访问违例:

type
  TMyDataRec = record
    ProductID: Integer;
    DownloadURL: WideString;
    MaintenanceDate: AnsiString;
  end;

  TMyDataRecPtr = ^TMyDataRec;

procedure get_Record(var aRecordPtr: TMyDataRecPtr);
var
  iData: TMyDataRec;
begin
  iData.ProductID       := 1;
  iData.DownloadURL     := 'www.thisisatest.com';
  iData.MaintenanceDate := '02/29/2023';

  aRecordPtr := <...>
end;

问题根源

你当前的iData是栈上的局部变量,过程执行完毕后栈空间会被系统回收。如果直接将局部变量的地址赋值给aRecordPtr,后续访问该指针会指向已释放的内存,必然引发访问违例。此外,记录中包含WideString和AnsiString这类托管字符串,需要确保内存分配逻辑正确,避免内存泄漏或野指针。

正确实现方案

必须在堆内存中分配记录空间,再将数据写入堆内存,最后返回堆内存的指针。以下是两种可靠的实现方式:

方式一:使用GetMem手动分配内存

type
  TMyDataRec = record
    ProductID: Integer;
    DownloadURL: WideString;
    MaintenanceDate: AnsiString;
  end;

  TMyDataRecPtr = ^TMyDataRec;

procedure get_Record(var aRecordPtr: TMyDataRecPtr);
begin
  // 分配堆内存,大小为记录的字节数
  GetMem(aRecordPtr, SizeOf(TMyDataRec));
  try
    // 直接初始化堆上的记录字段
    aRecordPtr^.ProductID := 1;
    aRecordPtr^.DownloadURL := 'www.thisisatest.com';
    aRecordPtr^.MaintenanceDate := '02/29/2023';
  except
    // 初始化失败时释放已分配的内存,避免泄漏
    FreeMem(aRecordPtr);
    raise;
  end;
end;

方式二:使用New简化分配(推荐)

New会自动处理记录的内存分配,若记录有构造函数也会自动调用:

procedure get_Record(var aRecordPtr: TMyDataRecPtr);
begin
  New(aRecordPtr);
  try
    aRecordPtr^.ProductID := 1;
    aRecordPtr^.DownloadURL := 'www.thisisatest.com';
    aRecordPtr^.MaintenanceDate := '02/29/2023';
  except
    // 初始化失败时释放内存
    Dispose(aRecordPtr);
    raise;
  end;
end;

关键注意事项

  • 内存释放:调用get_Record后,必须在使用完指针后手动释放内存,否则会造成内存泄漏:
    var
      MyRecPtr: TMyDataRecPtr;
    begin
      get_Record(MyRecPtr);
      try
        // 使用记录数据示例
        ShowMessage(MyRecPtr^.DownloadURL);
      finally
        // 对应GetMem用FreeMem,对应New用Dispose
        // FreeMem(MyRecPtr);
        Dispose(MyRecPtr);
      end;
    end;
    
  • 托管字符串处理:Delphi的WideString/AnsiString会自动管理自身内存,赋值给堆上的记录字段时,字符串的引用计数会正确更新,释放记录时无需额外处理字符串内存。
  • 禁止返回局部变量地址:绝对不能写aRecordPtr := @iData,局部变量在过程结束后会被销毁,指针会变成野指针。

内容的提问来源于stack exchange,提问作者John S.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:13:24