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.
相关产品推荐
相关产品推荐

