'Value never used' 更改为 'variable not initialized'
'Value never used' changes to 'variable not initialized'
如果我编译下面的代码,它会在第 1 行发出警告:
value assigned to lTime never used*
但是如果我删除该行,我会在第 2 行收到不同的警告:
variable lTime might not have been initialized
是编译器遗漏了什么,还是我遗漏了什么?
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
这不是对 Why is the Compiler warning that variable may not be initialized? 的欺骗,因为我也在异常中分配了 lTime
。
这是正确的行为。第 1 行的值永远不会被使用,因为在 try/except 中你分配了一个新值而不使用前一个值,因此出现警告。
如果您删除了您尝试在 try/except 块之外使用 lTime 变量的行,这可能会失败而实际上没有为 lTime
设置值
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
你可以这样做
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := StrToInt64Def(Copy(TSR.Name,1,pos('.',TSR.Name)-1), lCutOff);
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
lTime := lCutOff;
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
编译器对这段代码发出警告是正确的。当您在第一行分配 lTime := lCutOff
时,永远不会读取该分配中写入的值。
但是,当您删除代码时,事情就不那么清晰了。
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
有两种情况需要考虑:在 try/except
块内没有引发异常,或者在那里引发了异常。
如果没有引发异常则很简单,lTime
被赋值为 StrToInt64
的结果,因此在读取之前被初始化。
在引发异常时,lTime
不会在块内初始化。接下来会发生什么?
- 如果异常源自
Exception
(非强制性),则它会被捕获并初始化 lTime
。
- 如果异常不是从
Exception
派生的,那么它不会被捕获并且永远不会读取 lTime
。
因此,编译器可以推断出所有这些,因此不会发出未初始化的变量警告。但是,遗憾的是,编译器不会对这种复杂性进行流分析。我相信它的逻辑是这样运行的:
lTime
初始化前抛出异常,然后
- 可以捕获到异常,在这种情况下
- 然后读取变量
lTime
,但仍可能未初始化。
在第 2 步中,它应该能够意识到 lTime
已初始化,但它根本不执行此类分析。因此,尽管有人可能会争辩说编译器可以做得更好,但您只需要接受这是其分析算法的局限性。
了解到我们剩下的任务是找到一种方法来编写代码并避免警告。我们不想抑制警告,我们只需要找到一种方法来编写代码,使其既正确又没有警告。
在我看来,前进的方向是认识到异常是在这里使用的错误工具。此转换失败是正常行为。您应该使用在失败时不会引发异常的转换函数来编写代码。例如,您可以使用以下选项之一:
if TryStrToInt64(..., lTime) then
if lTime < lCutOff then
....
else
lTime := lCutoff;
或:
lTime := StrToInt64Def(..., lCutoff);
if lTime < lCutOff then
....
如果我编译下面的代码,它会在第 1 行发出警告:
value assigned to lTime never used*
但是如果我删除该行,我会在第 2 行收到不同的警告:
variable lTime might not have been initialized
是编译器遗漏了什么,还是我遗漏了什么?
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
这不是对 Why is the Compiler warning that variable may not be initialized? 的欺骗,因为我也在异常中分配了 lTime
。
这是正确的行为。第 1 行的值永远不会被使用,因为在 try/except 中你分配了一个新值而不使用前一个值,因此出现警告。
如果您删除了您尝试在 try/except 块之外使用 lTime 变量的行,这可能会失败而实际上没有为 lTime
设置值procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := lCutOff; // Line 1
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then // Line 2
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
你可以这样做
procedure TFormWebServices.RemoveOldReports;
var
TSR : TSearchRec;
I : Integer;
lCutOff,
lTime : Int64;
TSToDelete: TStringList;
S,Msg : String;
E : Exception;
begin
lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
I := FindFirst(FReportDir + '*.pdf',0,TSR);
TSToDelete := TStringList.Create;
while I = 0 do
begin
if (TSR.Attr and faDirectory) = 0 then
begin
lTime := StrToInt64Def(Copy(TSR.Name,1,pos('.',TSR.Name)-1), lCutOff);
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
end;
I := FindNext(TSR);
end;
lTime := lCutOff;
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
编译器对这段代码发出警告是正确的。当您在第一行分配 lTime := lCutOff
时,永远不会读取该分配中写入的值。
但是,当您删除代码时,事情就不那么清晰了。
try
lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
TSToDelete.Add(TSR.Name);
有两种情况需要考虑:在 try/except
块内没有引发异常,或者在那里引发了异常。
如果没有引发异常则很简单,lTime
被赋值为 StrToInt64
的结果,因此在读取之前被初始化。
在引发异常时,lTime
不会在块内初始化。接下来会发生什么?
- 如果异常源自
Exception
(非强制性),则它会被捕获并初始化lTime
。 - 如果异常不是从
Exception
派生的,那么它不会被捕获并且永远不会读取lTime
。
因此,编译器可以推断出所有这些,因此不会发出未初始化的变量警告。但是,遗憾的是,编译器不会对这种复杂性进行流分析。我相信它的逻辑是这样运行的:
lTime
初始化前抛出异常,然后- 可以捕获到异常,在这种情况下
- 然后读取变量
lTime
,但仍可能未初始化。
在第 2 步中,它应该能够意识到 lTime
已初始化,但它根本不执行此类分析。因此,尽管有人可能会争辩说编译器可以做得更好,但您只需要接受这是其分析算法的局限性。
了解到我们剩下的任务是找到一种方法来编写代码并避免警告。我们不想抑制警告,我们只需要找到一种方法来编写代码,使其既正确又没有警告。
在我看来,前进的方向是认识到异常是在这里使用的错误工具。此转换失败是正常行为。您应该使用在失败时不会引发异常的转换函数来编写代码。例如,您可以使用以下选项之一:
if TryStrToInt64(..., lTime) then
if lTime < lCutOff then
....
else
lTime := lCutoff;
或:
lTime := StrToInt64Def(..., lCutoff);
if lTime < lCutOff then
....