我只是想知道,为什么大多数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.
这里(http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html)是我关于此主题的笔记。 IMO,用默认值声明一个常数是更好的方法。
最佳答案
主要是历史原因。 FillChar()的历史可以追溯到Turbo Pascal的日子,并用于此类目的。这个名称确实有点用词不当,因为它虽然说Fill Char ojit (),但实际上是Fill 字节()。原因是最后一个参数可以使用char 或一个字节。因此FillChar(Foo,SizeOf(Foo),#0)和FillChar(Foo,SizeOf(Foo),0)是等效的。另一个令人困惑的原因是,从Delphi 2009开始,即使Char等效于WideChar,FillChar仍仅填充字节。在查看FillChar的最常见用法以确定大多数人是使用FillChar实际用字符数据填充内存还是仅使用它来使用给定的字节值初始化内存时,我们发现正是后者的情况主导了它的使用而不是前者。因此,我们决定使FillChar以字节为中心。
的确,如果未在适当的上下文中使用,用FillChar清除包含使用“托管”类型(字符串,Variant,Interface,动态数组)之一声明的字段的记录,这可能是不安全的。但是,在您给出的示例中,实际上是安全的,只要对该范围内的记录执行第一件事就是对本地声明的记录变量进行调用。原因是编译器已生成代码以初始化记录中的字符串字段。这将已经将字符串字段设置为0(无)。调用FillChar(Foo,SizeOf(Foo),0)只会覆盖0字节的整个记录,包括已经为0的字符串字段。在之后对记录变量使用FillChar,将值分配给了字符串字段,是不建议。使用初始化的常量技术是解决此问题的一个很好的方法,因为编译器可以生成正确的代码以确保在分配过程中正确地完成了现有记录值的确定。