【问题标题】:Avoiding "variable might not have been initialized"避免“变量可能尚未初始化”
【发布时间】:2010-04-26 22:29:42
【问题描述】:

我最近遇到了一个看起来像这样的例程:

procedure TMyForm.DoSomething(list: TList<TMyObject>; const flag: boolean);
var
  local: integer;
begin
  if flag then
    //do something
  else local := ExpensiveFunctionCallThatCalculatesSomething;

  //do something else
  for i := 0 to list.Count do
    if flag then
      //do something
    else if list[i].IntValue > local then //WARNING HERE
        //do something else
end;

这会给出Variable 'local' might not have been initialized,即使您可以通过阅读代码来判断除非初始化它的代码分支已经运行,否则您不会点击该行。

现在,我可以通过在程序顶部添加一个无用的local := 0; 来消除此警告,但我想知道是否可能没有更好的方法来构造它以避免此问题。有人有什么想法吗?

【问题讨论】:

  • 我认为预初始化变量是一种很好的做法,因为它们指向内存中的随机数据,如果你不注意它可能会导致你的应用程序出现意外行为 :) C# 我认为不会甚至允许您运行该代码,因为本地未初始化。
  • 哦,我知道这是个好习惯。我只是想知道是否没有更好的方法来编写不需要初始化的代码以避免收到警告。 (关闭警告是作弊。)
  • 我猜编译器并没有那么复杂。它可能只是看到本地可能被初始化。
  • “即使你可以通过阅读代码知道除非初始化它的代码分支已经运行,否则你不会到达那一行”——编译器很聪明,但不是那么聪明。这是您正在做的一件微妙的事情,以确保 local 如果未初始化则永远不会被访问,并且它会在警告方面出错。
  • 确实应该有人指出循环计数器上的索引溢出。 ;)

标签: delphi refactoring compiler-warnings


【解决方案1】:

我会将它分成两个 for 循环——一个用于 flag 为 true 时,一个用于 flag 为 false 时。另外一个好处是,您不必在每次迭代时都执行 if 语句。

【讨论】:

  • 我会说不太可能,但是您当然可以用两种方式编写例程,然后比较每种情况下代码的 CPU 反汇编视图,以自行确定。没必要 :wonder: ;)
  • 是的,这是一个不错的性能优化,但我看不出它如何消除编译器警告。
  • 因为在“if NOT flag”分支中,变量会在使用前被初始化,而在替代流程中它根本不会被使用(或引用)。
  • @Mason,它应该消除警告,因为在 flag=true 分支中,您永远不会尝试访问 local 的值。试试看。
【解决方案2】:

IMO,分配给 0 在这里并不是没有用的 - 它有利于可维护性。因此,您将不必花一两分钟来确定代码是否有效,从而使某人(可能是您未来的自己)免于费力。他们可能会失去设计的聪明才智(即使是你!)

【讨论】:

  • 有时这个赋值是有害的,因为它关闭了编译器,但这只是隐藏了一个语义错误:假设 Mason 的继任者错误地将 if 条件替换为 if not flag then。然后警告将是合理的,但local := 0; 会抑制它。所以在这种情况下,我认为它甚至不是无用的,而是一种悲观。
  • 我见过添加初始化消除虚假might not have been initialized 警告的情况,只是产生另一个警告value assigned to x is never used - 似乎编译器有时无法将两个和两个放在一起。
【解决方案3】:

根据标志参数重构代码以包含两个独立的流程:

procedure TMyForm.DoSomething(list: TList<TMyObject>; const flag: boolean);
var
  local: integer;
begin
  if flag then
  begin
    //do something
    //do something else
    for i := 0 to Pred(list.Count) do
      //do something
  end
  else
  begin
    local := ExpensiveFunctionCallThatCalculatesSomething;

    //do something else
    for i := 0 to Pred(list.Count) do
      if list[i].IntValue > local then
        //do something else
  end;
end;

这实质上重申了 neilwhitaker1 给出的答案,但也明确了 local 变量的初始化将被带入条件分支,这就是解决编译器警告(仅当变量在可能未初始化的分支中使用时才会发出 - 在根本不使用它的分支中不应发出此类警告,并且在使用它的分支中是肯定会被初始化,并且由于它在一个分支中使用,因此您也不会得到“可能不被使用”的提示。

注意:如果任何“// something else”对每个分支都是通用的,那么这些当然可以重构为本地嵌套过程以避免重复。

另请注意:在上面的代码中,我已经更正了 for 循环上的循环索引溢出。 :)

【讨论】:

  • 补充说明:这两个分支看起来很不一样。也许将其拆分为两个函数:“if flag then DoSomethingSimple else DoSomethingExpensive;”
  • 这对原作者来说是一个进一步的重构决定——如果不知道“某物”和“其他东西”是什么,就不可能说这两个分支有多相似。 :)
【解决方案4】:

添加 local:=0 好的解决方案。

我知道提示,因为可以是未初始化的变量。如果您总是为初始化分配变量值并使用 try finally 块进行错误检查,那么您就不会遇到任何问题。

据我所知,编译器会给出提示,因为即使您在运行时检查标志,您的变量也可能未分配并且您的代码无法按预期运行。

对不起我的英语不好:)

【讨论】:

    【解决方案5】:

    我会将您的程序的顶部更改为

    procedure TMyForm.DoSomething(list: TList<TMyObject>; const flag: boolean);
    var
      local: integer;
    begin
      if flag then
       begin
         local := 0;
         //do something
       end
      else local := ExpensiveFunctionCallThatCalculatesSomething;
    

    等等……

    这样无论 flag 是什么,Local 都会被设置,如果 flag 为 false,它不会被设置两次

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-25
      • 2015-07-04
      • 1970-01-01
      相关资源
      最近更新 更多