short-circuit evaluation and assignment operators

Discussion in 'C Programming' started by Anthony Paul, Jun 6, 2009.

  1. Anthony Paul

    Anthony Paul Guest

    Hello everyone,

    I have the following code for testing my expression evaluator (I know
    the code doesn't make sense, but it doesn't have to for the purposes
    of this test) :

    int Z = 5;
    int e = Z || Z <<= Z;

    My evaluator first evaluates Z, sees that it's true, and doesn't
    evaluate the Z <<= Z part. So far so good. Now if I set Z = 0, it
    would check Z, find it to be false, and then evaluate Z <<= Z and find
    that false as well.

    When I test the same code on either gcc or Visual Studio's C++
    compiler, I get the "error: lvalue required as left operand of
    assignment" error message during compilation. This would make sense if
    Z || Z was evaluated first, and then it tried to apply Z <<= Z to the
    result of that evaluation, but that can't be now can it? The short-
    circuit evaluator clearly separates the expression into two camps :

    Camp A : Z
    ||
    Camp B : Z <<= Z

    The above should clearly not produce the lvalue required error
    message; Z IS an lvalue.


    And yet the only possible way I can explain its reasoning is if it
    were evaluating the expression as follows :

    Camp A : Z || Z
    Camp B : (result of Z || Z) <<= Z

    But if that's the case, how could it possibly do short-circuit
    evaluation?

    Strange...

    Warm regards,

    Anthony
    Anthony Paul, Jun 6, 2009
    #1
    1. Advertising

  2. Anthony Paul wrote:
    > int Z = 5;
    > int e = Z || Z <<= Z;

    <snip>
    > When I test the same code on either gcc or Visual Studio's C++
    > compiler, I get the "error: lvalue required as left operand of
    > assignment" error message during compilation. This would
    > make sense if Z || Z was evaluated first,


    It's a matter of parsing, not evaluation. Whilst Bitwise Shift
    Operators
    have higher precedence than Logical OR, Assignment operators
    have a lower precedence.

    > ... the only possible way I can explain its reasoning is if it
    > were evaluating the expression as follows :
    >
    > Camp A : Z || Z
    > Camp B : (result of Z || Z) <<= Z
    >
    > But if that's the case, how could it possibly do short-circuit
    > evaluation?


    It can't. Write it as...

    e = Z || (Z <<= Z);

    --
    Peter
    Peter Nilsson, Jun 6, 2009
    #2
    1. Advertising

  3. Anthony Paul

    luserXtrog Guest

    On Jun 6, 12:03 am, Anthony Paul <> wrote:
    > Hello everyone,
    >
    > I have the following code for testing my expression evaluator (I know
    > the code doesn't make sense, but it doesn't have to for the purposes
    > of this test) :
    >
    > int Z = 5;
    > int e = Z || Z <<= Z;
    >
    > My evaluator first evaluates Z, sees that it's true, and doesn't
    > evaluate the Z <<= Z part. So far so good. Now if I set Z = 0, it
    > would check Z, find it to be false, and then evaluate Z <<= Z and find
    > that false as well.
    >
    > When I test the same code on either gcc or Visual Studio's C++
    > compiler, I get the "error: lvalue required as left operand of
    > assignment" error message during compilation. This would make sense if
    > Z || Z was evaluated first, and then it tried to apply Z <<= Z to the
    > result of that evaluation, but that can't be now can it? The short-
    > circuit evaluator clearly separates the expression into two camps :
    >
    > Camp A : Z
    > ||
    > Camp B : Z <<= Z
    >
    > The above should clearly not produce the lvalue required error
    > message; Z IS an lvalue.
    >
    > And yet the only possible way I can explain its reasoning is if it
    > were evaluating the expression as follows :
    >
    > Camp A : Z || Z
    > Camp B : (result of Z || Z) <<= Z
    >
    > But if that's the case, how could it possibly do short-circuit
    > evaluation?
    >
    > Strange...
    >
    > Warm regards,


    Assignments have lower precedence that logical operators.
    This includes <<=. So the expression is parsed like your
    second example. Like this:

    e = ((Z || Z) <<= Z);

    Now if the first Z is non-zero, it most certainly could avoid
    evaluating the second Z. But the result of the logical operator
    is not something that can be stored into.

    You'll need explicit parens to explain this to the compiler.
    The default rules interpret your expression as nonsense.
    You could try:

    e = (Z || (Z <<= Z));

    But, of course, since the right hand size is short-circuited
    unless Z == 0, what's the sense in rotating 0 zero times?

    --
    lxt
    luserXtrog, Jun 6, 2009
    #3
  4. Anthony Paul

    Anthony Paul Guest

    Hey guys,

    Thanks for replying!

    I see both of your points, and I think I understand now but I want to
    clarify. Are you guys saying that whether short-circuit evaluation is
    performed depends on precedence? If so, I get what you're saying.

    To make sure I got this straight, the following :

    Z || Z <<= Z

    is evaluated as as

    (Z || Z) <<= Z

    and that's because the <<= operator has lower precedence. I imagine
    that the short-circuit happens between the two Z's now, it evaluates
    the first Z and finds it to be true (1) and doesn't evaluate the
    second Z. It then does 1 <<= Z which is obviously an error. Got it.

    Now in the case of :

    Z || Z / 0

    This compiles fine and evaluates to a 1. I would imagine that it is
    evaluating this as :

    Z || (Z / 0)

    because the division operator has higher precedence. Now the short-
    circuit happens completely different from the first example.... it's
    now between the Z and the (Z / 0). Since the first Z evaluates to true
    (1) it doesn't bother doing the rest.

    If what I just said makes sense, then I understand now and I thank you
    both! If not, then God help me! :p

    Regards,

    Anthony
    Anthony Paul, Jun 6, 2009
    #4
  5. On Fri, 5 Jun 2009 23:02:50 -0700 (PDT), Anthony Paul
    <> wrote:

    >Hey guys,
    >
    >Thanks for replying!
    >
    >I see both of your points, and I think I understand now but I want to
    >clarify. Are you guys saying that whether short-circuit evaluation is
    >performed depends on precedence? If so, I get what you're saying.


    No, short circuit evaluation does not depend of precedence. The short
    circuit operators always perform short circuit evaluations.

    What depends on precedence is which parts of the expression are the
    actual operands of the || operator. You thought one operand was Z and
    the other was Z<<=Z. Due to the precedence, the left operand was the
    first Z and the right operand was the second Z.

    There is no language problem with this. The problem occurs because
    the result of Z||Z was used as the left operand of the <<= operator.

    You got it right below.

    >
    >To make sure I got this straight, the following :
    >
    >Z || Z <<= Z
    >
    >is evaluated as as
    >
    >(Z || Z) <<= Z
    >
    >and that's because the <<= operator has lower precedence. I imagine
    >that the short-circuit happens between the two Z's now, it evaluates
    >the first Z and finds it to be true (1) and doesn't evaluate the
    >second Z. It then does 1 <<= Z which is obviously an error. Got it.
    >
    >Now in the case of :
    >
    >Z || Z / 0
    >
    >This compiles fine and evaluates to a 1. I would imagine that it is
    >evaluating this as :
    >
    >Z || (Z / 0)
    >
    >because the division operator has higher precedence. Now the short-
    >circuit happens completely different from the first example.... it's
    >now between the Z and the (Z / 0). Since the first Z evaluates to true
    >(1) it doesn't bother doing the rest.


    --
    Remove del for email
    Barry Schwarz, Jun 6, 2009
    #5
  6. Anthony Paul

    Guest

    On 6 June, 07:02, Anthony Paul <> wrote:
    > Hey guys,
    >
    > Thanks for replying!
    >
    > I see both of your points, and I think I understand now but I want to
    > clarify. Are you guys saying that whether short-circuit evaluation is
    > performed depends on precedence? If so, I get what you're saying.


    Barry Schwarz has commented on this.

    > To make sure I got this straight, the following :
    >
    > Z || Z <<= Z
    >
    > is evaluated as as
    >
    > (Z || Z) <<= Z
    >
    > and that's because the <<= operator has lower precedence. I imagine
    > that the short-circuit happens between the two Z's now, it evaluates
    > the first Z and finds it to be true (1) and doesn't evaluate the
    > second Z. It then does 1 <<= Z which is obviously an error. Got it.
    >
    > Now in the case of :
    >
    > Z || Z / 0
    >
    > This compiles fine and evaluates to a 1. I would imagine that it is
    > evaluating this as :
    >
    > Z || (Z / 0)
    >
    > because the division operator has higher precedence. Now the short-
    > circuit happens completely different from the first example.... it's
    > now between the Z and the (Z / 0). Since the first Z evaluates to true
    > (1) it doesn't bother doing the rest.


    Yes, these examples look fine.

    Part of the problem is that computing is not the same as maths,
    despite using similar terms. For instance, in maths, if you do:

    x = 3 + 4 * 5

    then to say "the addition uses the result of the multiplication" is
    much the same thing as saying "you do the multiplication first".
    However, these are slightly different concepts in computing. The
    question of which operators operate on which values is called
    precedence (unless you're very pedantic). The question of which
    calculation is done when is called order of execution.

    Here's a particularly tricky example:

    x = y || (4+5);

    In this case, the brackets give the addition a higher precedence
    (though they're not strictly needed as + has a higher precedence than
    || anyway). So the values that || acts on are, on the one hand, y, and
    on the other hand, 4+5. That's precedence. BUT, || works by checking
    the first value and only evaluating the second value if needed - so
    here, if y is not zero, 4 and 5 aren't added at all. So || starts
    operating *before* the addition is carried out.

    Once you follow that, everything else will be a walk in the park...

    Hope that helps.
    Paul.
    , Jun 6, 2009
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Phil...
    Replies:
    10
    Views:
    716
    Joona I Palaste
    Oct 23, 2003
  2. webposter
    Replies:
    2
    Views:
    636
    Peter Shaggy Haywood
    Sep 14, 2004
  3. Lassie

    short circuit evaluation

    Lassie, Sep 16, 2008, in forum: C Programming
    Replies:
    29
    Views:
    807
  4. mingze zhang
    Replies:
    2
    Views:
    458
    James Kanze
    Jul 15, 2010
  5. mingze zhang
    Replies:
    4
    Views:
    1,103
    aadilsabri
    Oct 22, 2011
Loading...

Share This Page