在记录类型之间分配字符串时Delphi访问冲突错误

mad*_*moe 5 delphi pointers access-violation data-structures getmem

我有一个简单的记录类型.我分配了此记录的新实例,并使用过程("_ clone")将值从现有记录复制到新记录.我只在分配字符串值时才获得访问冲突.

有任何想法吗?非常感谢帮助.


TYPE定义:

TPointer = ^TAccessoryItem;
TAccessoryItem = Record
  Id : Integer;
  PartNumber : String;
  Qty : Integer;
  Description : String;
  Previous : Pointer;
  Next : Pointer;
end;
Run Code Online (Sandbox Code Playgroud)
Procedure TAccessoryList._clone (Var copy : TAccessoryItem; Var original : TAccessoryItem);

 begin

    copy.Id := original.Id;
    copy.Qty := original.Qty;
    copy.Partnumber := original.Partnumber;  **// Access errors happens here**
    copy.Next := Nil;
    copy.Previous := Nil;

  end;
Run Code Online (Sandbox Code Playgroud)

致电以下申请:

  procedure TAccessoryList.AddItem(Var Item : TAccessoryItem);

 Var

    newItem : ptrAccessoryItem;

 begin

    GetMem(newItem, sizeOf(TAccessoryItem));

    _clone(newItem^, Item);

 end;
Run Code Online (Sandbox Code Playgroud)

dth*_*rpe 20

您需要使用零初始化新结构.GetMem不会将分配的内存归零,因此记录的字段最初包含随机垃圾.你需要打电话

FillChar(newItem^, sizeof(TAccessoryItem), 0)

在GetMem之后,在使用记录之前.

原因如下:当您分配给新分配但未初始化的记录的字符串字段时,RTL会看到目标字符串字段不为空(包含垃圾指针)并尝试取消引用字符串以在分配新字符串之前减少其引用计数字符串值到该字段.这对于字符串字段或变量的每个赋值都是必需的,这样在将新值分配给字符串字段或变量之前,如果没有其他字符串使用它,则将释放先前的字符串值.

由于指针是垃圾,你会遇到访问冲突...如果你很幸运.随机垃圾指针有可能巧合地是指向进程中分配的地址范围的值.如果发生了这种情况,你就不会在转让时获得AV,但是在程序执行后期可能会出现更糟糕且更加神秘的崩溃,因为对未初始化变量的字符串赋值会在进程中的其他位置改变内存不当.

每当您直接处理内存指针并且您正在分配的类型包含编译器管理字段时,您需要非常小心地初始化和处置类型,以便编译器管理字段被初始化并正确处理.

在Delphi中分配记录的最佳方法是使用New()函数:

New(newItem);
Run Code Online (Sandbox Code Playgroud)

编译器将根据指针类型(指针类型指向的大小)推断分配大小,分配内存,并为您适当初始化所有字段.

相应的deallocator是Dispose()函数:

Dispose(newItem);
Run Code Online (Sandbox Code Playgroud)

除了释放记录本身使用的内存之外,这还将确保正确处理记录的所有编译器管理字段.

如果只是FreeMem(newItem),则会泄漏内存,因为该记录中的字符串和其他编译器管理字段占用的内存不会被释放.

编译器管理类型包括长字符串("字符串",而不是"字符串[10]"),宽字符串,变体,接口,可能是我忘记的东西.

需要考虑的一种方法是:GetMem/FreeMem只需分配和释放内存块.他们对如何使用该块一无所知.但是,New和Dispose"了解"您正在分配或释放内存的类型,因此他们将进行任何额外的工作以确保自动处理所有内部内务处理.

通常,最好避免使用GetMem/FreeMem,除非您真正需要的是一个原始内存块,没有与之关联的类型语义.

  • 我建议`Initialize`超过`FillChar` - "没有与之关联的类型语义的原始内存块." 而你忘记的是动态数组. (2认同)