Delphi 中的布尔表达式求值顺序?

Boolean expression order of evaluation in Delphi?

if exp1 and exp2 and exp3 then
    //something

在Delphi中求值的顺序exp1,exp2和exp3(当然都是Boolean)是定义的还是随机的?

评价顺序只在Boolean Short-Circuit Evaluation is enabled. Then, as the documentation in Complete Versus Short-Circuit Boolean Evaluation解释时定义,评价顺序是从左到右。

如果未启用布尔短路评估,则订单未定义。以下代码演示了这一点:

{$APPTYPE CONSOLE}
    
function A: boolean;
begin
  Result := True;
  Write('A ');
end;
    
function B: string;
begin
  Result := '';
  Write('B ');
end;
    
function C: Integer;
begin
  Result := 0;
  Write('C ');
end;
    
begin
  {$O+}
  Writeln('short circuit on');
  {$B-}
  if A and (B = '') and (C = 0) then Writeln;
  Writeln('short circuit off');
  {$B+}
  if A and (B = '') and (C = 0) then Writeln;
end.

对于第一个,您将打印 A B C,对于第二个,您将打印 B A C

它是为 Win32 编译的——为了让它变得有趣,并带回它未定义的意义,让它在 Win64 上 运行 它在两种情况下我们都得到 A B C

你可能会说:“好吧,但也许 B 被先调用了,但是布尔表达式 B = '' 的计算是以正确的顺序计算的。”我们来看看执行的汇编代码:

if A and (B = '') and (C = 0) then Writeln;
0040B17C 8D45E8           lea eax,[ebp-]
0040B17F E864EAFFFF       call B
0040B184 837DE800         cmp dword ptr [ebp-],[=11=]
0040B188 0F94C3           setz bl
0040B18B E81CEAFFFF       call A
0040B190 22D8             and bl,al
0040B192 E891EAFFFF       call C
0040B197 85C0             test eax,eax
0040B199 0F94C0           setz al
0040B19C 22D8             and bl,al

没有:

  • 调用 B,与 ''
  • 进行比较
  • 调用Aand它与字符串比较的结果
  • 调用C,与0比较,and它与前面and
  • 的结果

转换为(按从左到右的顺序书写):

((B = '') and A) and (C = 0)

补充:因为看到另一个回答里有提到,在评论里讨论过。括号不是强制操作数求值顺序的解决方案。您只能对操作进行分组,但编译器可能仍会决定首先评估正确的操作数。 在上面的代码中,没有办法放置任何括号来获得 A B C ,因为编译器翻转了前两个操作数。然而,它然后从左到右执行运算符,这很容易混淆 - 问题不是第一个或第二个 and 先执行,而是布尔表达式的顺序 - 操作数。

文档当然会指导您。

我发现,在更复杂的 if 语句中,即使是有经验的程序员在编写代码时,编译器也不会执行预期的操作,这通常是因为它们包括逻辑非以及混合布尔值与和或。记住优先级和关联规则并不总是那么容易。

我的建议是明确说明您想要什么,并在条款两边加上括号。编译器不会介意,当您或其他人审查代码时,它会清楚说明您的意图。

使用复合 if 语句将逻辑部分放在一起也可以帮助人们理解其含义。

例如:

  if( ((pObjectRef<>nil) And (pObjecRef.Property=SomeValue)) Or
      ((pAlternateObject<>nil) And 
       ((pAlternatObject.Property=AnotherValue) Or 
        (pAlternatObject.Property=YetAnotherValue))) ) then
  begin
    ...
  end