考虑我有一个通过引用传递Str
参数的过程,我想通过该过程修改给定变量的内容,例如
procedure Replace(var Str: string);
var
PStr: PChar;
i: Integer;
begin
PStr := @Str[1];
for i := 1 to Length(Str) do begin
PStr^ := 'x';
Inc(PStr);
end;
end;
它是否可以接受指针使用?我不确定它是否有内存泄漏。
PStr := @Str[1]
中真正发生了什么,编译器是否在内部复制了Str
,或者是什么?
这种代码优化值得吗?
答案 0 :(得分:5)
是否可以使用指针?
你需要确保不要打电话
PStr := @Str[1];
表示空字符串,因为它会崩溃。最简单的方法是用
替换该行PStr := PChar(Str);
这样编译器将确保返回指向字符串的第一个char的指针或指向#0
的指针。正如Ken在评论中正确指出的那样,在这种情况下没有调用UniqueString()
,所以你需要自己做。
我不确定它是否有内存泄漏。
不,没有内存泄漏。获取指向字符串字符的指针将在内部调用UniqueString()
,但对于字符串字符的写访问也会发生这种情况,因此字符指针没有什么特别之处。
PStr中真正发生了什么:= @Str [1],编译器是否在内部制作了Str的副本,或者是什么?
不,它只是确保字符串是唯一的(因此通过指针的写访问不会更改共享相同数据的任何其他字符串的内容)。然后它返回一个指向字符串中该字符的指针,然后您可以将其视为任何其他PChar
变量,将其传递给API函数,递增它等等。
这种代码优化值得吗?
不仅值得,还需要真正实现大字符串的良好性能。这样做的原因是编译器不够智能,只能调用UniqueString()
一次,但是对于字符串中的字符的每次写访问,它都会插入调用。因此,如果逐个字符处理大字符串,那么所有这些调用都会产生很大的开销。
答案 1 :(得分:3)
是的,只要你没有超出字符串的范围,它就是安全的。该字符串附有元数据,告诉它有多长,如果你写的超出字符串的长度,你不会泄漏内存,但你可能会破坏它。
答案 2 :(得分:1)
如果通过引用传递Str
,为什么还需要另一个指向字符串的指针?除此之外,应该没有内存泄漏:PStr
使用字符串的第一个元素的地址进行初始化,然后递增,因此它将始终指向字符串中的一个字符。
编译不会在内部复制Str
。指针的一个用途是避免复制。当你说
PStr := @Str[1]
PStr
现在会存储Str[1]
的地址,即字符串中第一个字符的地址。
答案 3 :(得分:1)
我确信这适用于AnsiString和PAnsiChar,但是它仍适用于Delphi 2009及更高版本的unicode字符串吗?我认为它应该,因为两者都是字符串的字符串(str [i])和PChar指向的字符,应该是2个字节。
对于拥有更多unicode字符串经验的人,请确认一下吗?
答案 4 :(得分:-1)
与D2010一样,看起来像codegen在这样的构造
上使用了写时复制Unit9.pas.34: S := 'abcd';
004B32EF 8D45F4 lea eax,[ebp-$0c]
004B32F2 BA98334B00 mov edx,$004b3398
004B32F7 E89C35F5FF call @UStrLAsg
Unit9.pas.35: P := @S[1];
004B32FC 8D45F4 lea eax,[ebp-$0c]
004B32FF E8343FF5FF call @UniqueStringU ; <== here you are
004B3304 8945F0 mov [ebp-$10],eax
Unit9.pas.36: Exit;
004B3307 EB61 jmp $004b336a
顺便说一下,泛型引用P := @S
不会发出UniqueString。
作为结论,我不建议依靠codegen的内部结构并使用推荐的PChar(S)
构造(发出一个xStrToPxChar
调用作为开销)