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

Delphi中Packed Record数组跨实例加载失败问题求助

问题根源分析

你遇到的核心问题是保存的是指针地址而非实际的记录数据。在同一个程序实例中,rates_M1里的指针指向的内存还存在,所以加载后能正常访问;但程序重启后,这些旧的指针地址已经无效(内存被回收或重新分配),此时访问这些指针指向的内容自然会出错。

看一下你保存数据的关键代码:

Stream.WriteBuffer(Pointer(rates_M1)^, L * SizeOf(ARate));

这里SizeOf(ARate)是指针的大小(32位系统是4字节,64位是8字节),你写入的是700万个指针的地址值,而不是每个指针指向的TRate记录的实际数据。

解决方案

根据你的需求,有两种靠谱的解决方式:

方案1:改用记录数组(推荐,更简单)

直接使用array of TRate替代array of PRate,这样数组里存储的是实际的记录数据,而非指针。修改后代码更简洁,也避免了内存管理的麻烦:

修改初始化与保存代码

procedure TForm4.InitSaveAndReload(Sender: TObject);
var
  _start : TDatetime;
  filename : string;
  Stream : TFileStream;
  i,L : integer;
  rates_M1 : array of TRate; // 替换指针数组为记录数组
begin
  filename := edFile.Text;
  MemoLogs.Lines.Add('Initialization of 7 million array of records... Please wait.');
  Refresh;
  // 初始化数组
  _start := Now;
  SetLength(rates_M1, 7000000);
  for i:= 0 to 6999999 do
  begin
    // 直接操作数组内的记录,无需分配指针
    rates_M1[i].time := DateTimeToUnix(IncMinute(Now, i));
    rates_M1[i].open := 1.25698;
    rates_M1[i].low := 1.2574;
    rates_M1[i].high := 1.2547;
    rates_M1[i].close := 1.65874;
    rates_M1[i].tick_volume := 154;
    rates_M1[i].spread := 5;
    rates_M1[i].real_volume := 15741;
  end;
  MemoLogs.Lines.Add(IntToStr(Length(rates_M1)) + ' array of records initialized ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  // 保存数组
  _start:= Now;
  Stream:= TFileStream.Create(filename , fmCreate);
  try
    L:= Length(rates_M1);
    Stream.WriteBuffer(L, SizeOf(L));
    // 写入实际记录数据,SizeOf(TRate)是单个记录的完整大小
    Stream.WriteBuffer(rates_M1[0], L * SizeOf(TRate));
  finally
    Stream.Free;
    MemoLogs.Lines.Add(IntToStr(Length(rates_M1)) + ' records saved to disk in ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  end;
  LoadFromFile();
end;

修改LoadFromFile代码

procedure TForm4.LoadFromFile;
var
  _start : TDatetime;
  filename : string;
  Stream : TFileStream;
  i,L : integer;
  rates : array of TRate; // 替换指针数组为记录数组
begin
  // 重新加载数组
  _start := Now;
  filename := edFile.Text;
  Stream:= TFileStream.Create(filename , fmOpenRead);
  try
    Stream.Read(L, SizeOf(L));
    SetLength(rates, L);
    // 读取实际记录数据
    Stream.Read(rates[0], L * SizeOf(TRate));
  finally
    Stream.Free;
    MemoLogs.Lines.Add(IntToStr(Length(rates)) + ' records loaded from disk in ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  end;
  // 打印前20条记录
  MemoLogs.Lines.Add('Print 20 first records just reloaded in another array of records!' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  for i := 0 to 20 do
    MemoLogs.Lines.Add('i=' + IntToStr(i) + #9 + IntToStr(rates[i].time) + #9 + FloatToStr(rates[i].open) + #9 );
end;

方案2:如果必须使用指针数组(不推荐,除非有特殊内存需求)

如果你因为内存限制必须使用指针数组,那保存时需要逐个写入每个指针指向的TRate数据,读取时逐个分配内存并写入数据:

修改保存代码

// 保存时遍历指针数组,写入每个记录的实际内容
Stream.WriteBuffer(L, SizeOf(L));
for i := 0 to L-1 do
begin
  Stream.WriteBuffer(rates_M1[i]^, SizeOf(TRate));
end;

修改读取代码

// 读取时先获取长度,再逐个分配内存并读取数据
Stream.Read(L, SizeOf(L));
SetLength(rates, L);
for i := 0 to L-1 do
begin
  New(rates[i]);
  Stream.Read(rates[i]^, SizeOf(TRate));
end;

⚠️ 注意:这种方式需要记得在使用完数组后,遍历指针数组逐个调用Dispose(rates[i]),避免内存泄漏。

额外注意事项
  • 你的TRate是packed record,这非常好,确保了记录在内存中的布局紧凑,没有字节对齐的问题,跨实例/平台读取不会出现数据错位。
  • 如果后续需要扩展记录字段,建议在文件开头写入一个版本号,方便后续兼容旧版本的文件。

内容的提问来源于stack exchange,提问作者och

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:34:41