变量可能未被初始化。我能为字符串打开这个警告吗?

7
当我编译这段代码时
{$WARNINGS ON}
function Test(s: string): string;
var
  t: string;
  d: double;
begin
  if s = '' then begin
    t := 'abc';
    d := 1;
  end;

  Result := t + FloatToStr(d);
end;

我收到了警告 "变量'd'可能未被初始化",但对于变量't',我没有收到相同的警告。这似乎是不一致的。这段代码仅是一个简单的示例,用于展示编译器警告,但我在我的实际代码中发现了一个错误,未初始化的字符串变量会受到编译时警告检测。我可以在Delphi 6中开启此警告吗?或者在更新的Delphi版本中?


1
我非常怀疑现有的代码是否能编译通过。你在"t:= 'abc'"之前缺少一个'begin'。 - No'am Newman
糟糕,错别字已经更正 :) 我是在 Stack Overflow 上打的,而不是复制粘贴。 - soid
1
@TDelphiHobbyist 你有没有读懂问题和代码?!! - David Heffernan
@David,是的。我知道我的评论完全不切实际。在评论部分讨论观察结果是否有问题?我发表评论是因为它根本不是答案。如果有问题,我会停止。我原本要发帖“你为什么想要更多的警告?” - Dave Keighan
1
@TDelphiHobbyist:这个问题的核心在于简单类型会收到警告,但字符串不会。如果示例代码是以一种保证d被初始化的方式编写的,那么它作为一个人为制造的例子就没有什么意义了! - Disillusioned
显示剩余7条评论
2个回答

12

没有,这个没有开关。警告并不会出现是因为字符串是编译器管理的类型,并且总是由编译器初始化。


为什么你要担心它总是被初始化的事实? - Andriy M
有人知道如果您未初始化作为函数结果变量的托管类型是否会收到此类警告吗? - David Heffernan
@David:你不会收到这样的警告。例如:function StringResult: string; begin beep; end; 可以在没有提示或警告的情况下编译。(D2009) - Marjan Venema
1
@David:由于字符串是托管类型,因此函数Result会被初始化为该类型的一个空托管实例。请参见Barry Kelley的这个答案 - Jeroen Wiert Pluimers
@Jeroen 在某些情况下,Result 变量不会被初始化。例如,如果函数在循环中被调用且返回值未被分配给任何变量,则 Result 变量可能未被初始化。 - David Heffernan
显示剩余5条评论

3

是的 :-)

使用短字符串或pChars

{$WARNINGS ON}
function Test: String;
var
  p: pChar;
  d: double;
begin
  Result := p + FloatToStr(d);
end;
//This code will give a warning.

重要提示

普通的 Delphi 字符串和短字符串会自动初始化为空字符串 ''。短字符串存在于栈中,不需要清理。其他字符串是所谓的“托管”类型,在使用引用计数自动删除时不再使用。

PChars 的好消息
pChars 只是指针,Delphi 不会管理它们。
但是 Delphi 会自动将它们转换为字符串,反之亦然。

PChars 的坏消息
如果你将 pChar 转换为字符串,Delphi 将 pChar 的内容复制到字符串中,你仍然需要销毁 pChar。
此外,请注意,这种复制需要时间,如果你经常这样做,会减慢代码速度。

如果你将字符串转换为 pChar,Delphi 将给你一个指向字符串所在地址的指针。但是 Delphi 将停止管理该字符串。你仍然可以给字符串赋值,但它将不再自动增长。

来源:http://www.marcocantu.com/epascal/English/ch07str.htm

以下代码将不能按预期工作:

procedure TForm1.Button2Click(Sender: TObject);
var
  S1: String;
begin
  SetLength (S1, 100);
  GetWindowText (Handle, PChar (S1), Length (S1));
  S1 := S1 + ' is the title'; // this won't work
  Button1.Caption := S1;
end;

这个程序编译是可以通过的,但是当你运行它时,会有一个惊喜:按钮的标题将具有窗口标题的原始文本,而没有你添加到其中的常量字符串的文本。问题在于当Windows写入字符串(在GetWindowText API调用中)时,它没有正确设置长Pascal字符串的长度。Delphi仍然可以使用这个字符串进行输出,并且可以通过寻找空终止符来确定它何时结束,但是如果在空终止符后附加更多字符,则它们将被完全跳过。
我们如何解决这个问题?解决方案是告诉系统将GetWindowText API调用返回的字符串转换回Pascal字符串。然而,如果你编写以下代码:
S1 := String (S1);

系统会忽略它,因为将数据类型转换回自身是一项毫无意义的操作。要获取正确的长Pascal字符串,您需要将字符串重新转换为PChar,并让Delphi正确地将其转换回字符串:
S1 := String (PChar (S1));

实际上,您可以跳过字符串转换,因为在Delphi中,PChar到字符串的转换是自动的。这是最终代码:

procedure TForm1.Button3Click(Sender: TObject);
var
  S1: String;
begin
  SetLength (S1, 100);
  GetWindowText (Handle, PChar (S1), Length (S1));
  S1 := String (PChar (S1));
  S1 := S1 + ' is the title';
  Button3.Caption := S1;
end;

另一种方法是通过以下方式重置 Delphi 字符串的长度,使用 PChar 字符串的长度:

SetLength (S1, StrLen (PChar (S1)));

为什么会出现-1呢?这显然是一种幽默的表达方式,并且它确实说明了为什么没有警告以及哪些字符串会引发警告。 - Johan
你好Johan,我没有给你的帖子点踩。我猜可能是有人认为shortstring类型容易引入问题,因为它的长度限制为255个字符,所以最好完全忘记它。实际上,在我的情况下,你的代码会防止我发现的错误,因为我没有在字符串中使用超过255个字符。 - soid
@Andriy M:我刚刚尝试了一下,在Delphi 6中,shortstring似乎没有给我任何警告。我需要一个更新的Delphi版本吗?还是我做错了什么?实际上,这似乎是一个相当不错的主意,因为我可以拥有自己的字符串类型,有时可以将其从字符串切换到shortstring以进行警告,或者也许我可以对内置的字符串类型进行一些调整。 - soid
@大家:约翰是否误以为shortstring会产生警告?如果是这样,也许我可以创建一个具有所需属性(如'+')的类型,仅用于编译时警告目的? - soid
@soid,对于shortstrings我之前的说法是错误的,但你可以通过使用pChar来强制发出警告,请参见上面更新的答案。 - Johan
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接