Delphi 语句中带有AND运算符的成员是否始终按给定顺序进行检查?

Delphi 语句中带有AND运算符的成员是否始终按给定顺序进行检查?,delphi,logical-operators,Delphi,Logical Operators,我想知道以下代码是否会因访问冲突而失败,或者是否安全。带AND运算符的语句的第一个成员是否始终被检查为第一个,或者(通过某些编译器优化或其他方式)被检查为第二个成员是否为第一个 var Item: TSomething; procedure DoSomething; begin if Assigned(Item) and (Item.SomeProperty) then DoSomethingElse; end; 上面的代码绝对安全吗 谢谢 如果代码处于活动状态,则代码是安全

我想知道以下代码是否会因访问冲突而失败,或者是否安全。带AND运算符的语句的第一个成员是否始终被检查为第一个,或者(通过某些编译器优化或其他方式)被检查为第二个成员是否为第一个

var
  Item: TSomething;

procedure DoSomething;
begin
  if Assigned(Item) and (Item.SomeProperty) then
    DoSomethingElse;
end;
上面的代码绝对安全吗

谢谢

如果代码处于活动状态,则代码是安全的:

在{$B-}状态下,编译器生成用于短路布尔表达式求值的代码,这意味着只要整个表达式的结果按照从左到右的求值顺序变得明显,求值就会停止

这有点令人困惑,因为必须关闭
B
(或长名称的
BOOLEVAL
)指令才能打开短路评估


另请参见。

调用您提到的条件是安全的 看一看

我有一个字符串列表,我检查了这两个条件

    Strinlst : TStringlist; 

      Tester.pas.169: if ( (Assigned(Strinlst)) and(Strinlst.count<>6)) then
      0052A3CC 8BB3E4030000     mov esi,[ebx+$000003e4]
      0052A3D2 85F6             test esi,esi              //check if Strinlst is assigned
      0052A3D4 741F             jz $0052a3f5              //jump out if not true 
      0052A3D6 8BC6             mov eax,esi
      0052A3D8 8B10             mov edx,[eax]
      0052A3DA FF5214           call dword ptr [edx+$14]
      0052A3DD 83F806           cmp eax,$06               //compare the count
      0052A3E0 7413             jz $0052a3f5              //jump to the result
Strinlst:TStringlist;
Tester.pas.169:如果((分配(Strinlst))和(Strinlst.count6)),则
0052A3CC 8BB3E430000 mov esi,[ebx+$000003e4]
0052A3D2 85F6测试esi,esi//检查是否分配了Strinlst
0052A3D4 741F jz$0052a3f5//如果不正确,跳出
0052A3D6 8BC6 mov eax,esi
0052A3D8 8B10 mov edx,[eax]
0052A3DA FF5214呼叫dword ptr[edx+$14]
0052A3DD 83F806 cmp eax,$06//比较计数
0052A3E0 7413 jz$0052a3f5//跳转到结果
其他顺序中的条件也是如此

      Tester.pas.169: if ( (Strinlst.count<>6) and (Assigned(Strinlst)) ) then
      0052A3CB 8B83E4030000     mov eax,[ebx+$000003e4]
      0052A3D1 8B10             mov edx,[eax]
      0052A3D3 FF5214           call dword ptr [edx+$14]   //get the count
      0052A3D6 83F806           cmp eax,$06                //compare the count    
      0052A3D9 741B             jz $0052a3f6               //jump if not true  
      0052A3DB 83BBE403000000   cmp dword ptr [ebx+$000003e4],$00 //compare if Strinlst is assigned
      0052A3E2 7412             jz $0052a3f6              //jump if false
Tester.pas.169:如果((Strinlst.count6)和(赋值(Strinlst)),则
0052A3CB 8B83E430000 mov eax,[ebx+$000003e4]
0052A3D1 8B10 mov edx,[eax]
0052A3D3 FF5214调用dword ptr[edx+$14]//获取计数
0052A3D6 83F806 cmp eax,$06//比较计数
0052A3D9 741B jz$0052a3f6//如果不正确,则跳转
0052A3DB 83BBE403000000 cmp dword ptr[ebx+$000003e4],$00//比较是否分配了Strinlst
0052A3E2 7412 jz$0052a3f6//如果为假则跳转

因此,条件的顺序肯定是从左到右

这取决于您的Item.SomeProperty类型。如果它是变体,或者如果在它之前有一个要评估的变体,它将被评估并导致AV

编辑:忘记提及解决方法: 如果SomeProperty是Variant类型,则可以使用

  if Assigned(Item) and StrToBool(Item.SomeProperty) then
它确实花了一些时间将变量转换为字符串,然后再转换回布尔值,但至少它可以满足所有真/假/不存在的情况

下面是供您查看的测试用例:

unit Unit4;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Generics.Collections;

type
  TTestObj = class
  public
    V : Variant;
    I : Integer;
  end;

  TForm4 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    btn3: TButton;
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
    procedure btn3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    TOV : TTestObj;
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

procedure TForm4.btn1Click(Sender: TObject);
begin
  if Assigned(TOV) and (TOV.I = 10) then
    ShowMessage('You will never see this though no AV!');
end;

procedure TForm4.btn2Click(Sender: TObject);
begin
  if Assigned(TOV) and StrToBool(TOV.V) then
    ShowMessage('You will not see AV with StrToBool!');
  if Assigned(TOV) and TOV.V then
    ShowMessage('You will never see this but AVed!');
end;

procedure TForm4.btn3Click(Sender: TObject);
var
  V : Variant;
begin
  V := False;
  if Assigned(TOV) and V and (TOV.I = 10) then
    ShowMessage('You will see AV!');
end;

end.

这一指令根本就不应该看到曙光。到底是谁想要“完整”布尔求值?我猜短路表达式求值是后来添加的,作为一种优化,为了不破坏依赖于完整求值的现有代码,创建了编译器指令。根据我的经验(通过艰苦的学习),如果Item.SomeProperty是variant类型,将对其进行评估,并可能导致AV。然而,这么多人对答案投了赞成票,这让我很困惑……@ain&Ulrich。我认为短路评估一直是默认的,因此包含了关闭它的指令,而不是相反。在某些情况下,它是有用的;我在一些错误检查代码中只使用了一次,它没有在查找第一个错误时出错,而是查找所有错误并通知用户。使用短路求值功能,代码的可读性会降低。来自turbo pascal 5参考指南:布尔求值在与或布尔运算符的两种不同代码生成模式之间切换。在{$B+}状态下,编译器生成用于完整布尔表达式计算的代码。在{$B-}状态下,编译器生成用于短路布尔表达式求值的代码。
是运算符,而不是语句。您的示例可能会抛出空指针异常
$IFOPT B+
@user539484,很抱歉我的术语太弱:)我以为这是带有
运算符的语句。我会修好的。谢谢你的评论中隐藏的清晰答案。为什么不
如果分配(项目)那么如果item.someproperty然后
@PieterB,我知道它会更可读,但这只是因为好奇和我懒惰:-)@MartinReiner,没关系:-)我在这个问题上很迂腐,因为很多年前,我母语的编程文学翻译人员发展了一个丑陋的传统,用同一个词来表示语句和运算符。感谢沃思,在帕斯卡语中,它或多或少是可以忍受的,但在C语言中,它造成了整个毁灭性的混乱。这就是为什么这张非常重要的纸条如此重要!幸运的是,这些成员不是变量类型。@MartinReiner即使该成员不是变量,也要小心表达式中是否存在任何变量类型。如果赋值为(TOV)和(TOV.I=10),则。。。其中V为变量也将导致对V.I进行评估,并导致AV。我将更新答案以显示这一点。有趣的是,我不知道that@user539484我想逻辑是1。如果存在变量,则从右到左开始计算表达式以解析变量类型,当类型问题得到解决时,从左到右进行计算,并在确保结果时退出计算。所以,“如果分配(TOV)和V和(TOV.I=10)则”生成AV,而“如果分配(TOV)和(TOV.I=10)且V则”不生成AV。这似乎是合理的假设。很高兴知道,谢谢!