为什么大多数Delphi示例使用FillChar()来初始化记录?

sta*_*005 28 delphi initialization record

我只是想知道为什么大多数Delphi示例使用FillChar()来初始化记录.

type
  TFoo = record
    i: Integer;
    s: string; // not safe in record, better use PChar instead
  end;

const
  EmptyFoo: TFoo = (i: 0; s: '');

procedure Test;
var
  Foo: TFoo;
  s2: string;
begin
  Foo := EmptyFoo; // initialize a record

  // Danger code starts
  FillChar(Foo, SizeOf(Foo), #0);
  s2 := Copy("Leak Test", 1, MaxInt); // The refcount of the string buffer = 1
  Foo.s = s2; // The refcount of s2 = 2
  FillChar(Foo, SizeOf(Foo), #0); // The refcount is expected to be 1, but it is still 2
end;
// After exiting the procedure, the string buffer still has 1 reference. This string buffer is regarded as a memory leak.
Run Code Online (Sandbox Code Playgroud)

这里(http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html)是关于这个主题的注释.IMO,声明一个带有默认值的常量是一种更好的方法.

All*_*uer 36

历史原因,大多数.FillChar()可追溯到Turbo Pascal天,并用于此类目的.这个名字实际上有点用词不当,因为虽然它说Fill Char(),但实际上是Fill Byte().原因是最后一个参数可以采用char byte.所以FillChar(Foo,SizeOf(Foo),#0)和FillChar(Foo,SizeOf(Foo),0)是等价的.混淆的另一个原因是,从Delphi 2009开始,FillChar仍然只填充字节,即使Char等同于WideChar.在查看FillChar的最常见用途的同时,为了确定大多数人是否使用FillChar实际用字符数据填充内存,或者只是用它来用一些给定的字节值初始化内存,我们发现后一种情况主导了它的使用而不是前者.有了这个,我们决定保持FillChar以字节为中心.

确实,如果不在适当的上下文中使用,使用FillChar清除包含使用"托管"类型(字符串,变体,接口,动态数组)之一声明的字段的记录可能是不安全的.但是,在您给出的示例中,只要您对该范围内的记录执行的第一件事就是在本地声明的记录变量上调用FillChar实际上是安全的.原因是编译器已生成代码来初始化记录中的字符串字段.这已经将字符串字段设置为0(nil).调用FillChar(Foo,SizeOf(Foo),0)只会用0字节覆盖整个记录,包括已经为0的字符串字段.将值分配给字符串字段,在记录变量上使用FillChar ,不建议.使用初始化常量技术是解决此问题的一个非常好的解决方案,因为编译器可以生成正确的代码以确保在分配期间正确完成现有记录值.

  • >>>在将值分配给字符串字段后,在记录变量上使用FillChar,不建议在这种情况下使用Finalize(V); FillChar(V,SizeOf(V),0); 如果你经常这样做,你可以写一个新函数,它完成Finalize + FillChar.我认为使用FillChar会比分配常量更快,但今天它可能不相关.更重要的是 - 您不需要为每个记录的类型声明一个常量. (2认同)

LU *_* RD 18

如果您有Delphi 2009及更高版本,请使用该Default调用来初始化记录.

Foo := Default(TFoo); 
Run Code Online (Sandbox Code Playgroud)

请参阅David如何在Delphi中正确释放包含各种类型的记录的问题的回答.

编辑:

使用该Default(TSomeType)调用的优点是该记录在清除之前已完成.没有内存泄漏,也没有对FillChar或ZeroMem的明确危险的低级别调用.当记录复杂时,可能包含嵌套记录等,消除了犯错误的风险.

初始化记录的方法可以更简单:

const EmptyFoo : TFoo = ();
...
Foo := EmptyFoo; // Initialize Foo
Run Code Online (Sandbox Code Playgroud)

有时您希望参数具有非默认值,然后执行以下操作:

const PresetFoo : TFoo = (s : 'Non-Default'); // Only s has a non-default value
Run Code Online (Sandbox Code Playgroud)

这将节省一些打字,重点放在重要的东西上.

  • 感谢精彩的 () 构造,我不知道 Delphi 可以做到。 (2认同)

Fra*_*ois 9

FillChar可以确保你没有在一个新的,未初始化的结构(记录,缓冲区,arrray ......)中获得任何垃圾.
它不应该用于"重置"值而不知道你正在重置什么.
只是写作MyObject := nil并期望避免内存泄漏.
特别是要仔细观察所有管理类型.
请参阅Finalize功能.

当你有能力直接摆弄记忆时,总有一种方法可以用脚射击自己.