Unpredictable nature of increment operators

Discussion in 'C++' started by bintom, May 9, 2008.

  1. bintom

    bintom Guest

    Why doe the following C++ code behaves as it does?

    int i;

    i=1; cout << (++i)++; Output: 2
    i=1; cout << ++(++i); Output: 3
    i=1; cout << (i++)++; Output: Error. LValue required
    i=1; cout << ++(i++); Output: Error. LValue required
    bintom, May 9, 2008
    #1
    1. Advertising

  2. bintom

    Ian Collins Guest

    bintom wrote:

    Once is enough!

    --
    Ian Collins.
    Ian Collins, May 9, 2008
    #2
    1. Advertising

  3. bintom

    Martin York Guest

    On May 8, 5:16 pm, bintom <> wrote:
    > Why doe the following C++ code behaves as it does?
    >
    > int i;
    >
    > i=1; cout << (++i)++; Output: 2
    > i=1; cout << ++(++i); Output: 3
    > i=1; cout << (i++)++; Output: Error. LValue required
    > i=1; cout << ++(i++); Output: Error. LValue required


    Because code like that is so hard to read you would never actually
    write it!

    Also remember that it is illegal to modify a variable more than once
    within the same expression [undefined behavior I believe](somebody
    with a copy of the standard will be able to quote you chapter an verse
    and the actual definition rather than my layman's wording).
    Martin York, May 9, 2008
    #3
  4. bintom

    James Kanze Guest

    On 9 mai, 19:21, "Victor Bazarov" <> wrote:
    > Martin York wrote:
    > > On May 8, 5:16 pm, bintom <> wrote:
    > >> Why doe the following C++ code behaves as it does?


    > >> int i;


    > >> i=1; cout << (++i)++; Output: 2
    > >> i=1; cout << ++(++i); Output: 3
    > >> i=1; cout << (i++)++; Output: Error. LValue
    > >> required i=1; cout << ++(i++); Output: Error.
    > >> LValue required


    > > Because code like that is so hard to read you would never actually
    > > write it!


    > > Also remember that it is illegal to modify a variable more
    > > than once within the same expression [undefined behavior I
    > > believe](somebody with a copy of the standard will be able
    > > to quote you chapter an verse and the actual definition
    > > rather than my layman's wording).


    > Only a variable of a built-in type,


    No. The rule holds for all types.

    I think what is confusing you is the fact that if operator++ is
    user defined for the type of x, the expression ++x doesn't
    modify x; it calls a function (which might modify x---but that's
    a different issue, because the modification doesn't take place
    in this expression, and there are sequenece points when calling
    or returning from a function).

    In the case of operator++, of course, the operator is only
    defined for built-in types, so any other type will involve a
    function call. This isn't true for all operators, however.

    > and only if there is no sequence point between the
    > modifications.


    That's the key point: whether or not there is a sequence point.

    > For example, it's not undefined behaviour to do


    > f(i++) + ++i;


    Yes it is, since there is no sequence point between the two
    incrementations. Sequence points only define a partial
    ordering: there is a sequence point before calling f, but that
    only establishes and ordering between i++ and the call to f; it
    doesn't establish any ordering between the two incrementations.

    > IIRC, but the results are unspecified. And if your class has
    > the operators ++() or ++(int) overloaded, the results are even
    > well-defined and predictable.


    They're never well-defined and predictable. The compiler can
    call the functions in any order that it wants.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, May 10, 2008
    #4
  5. bintom

    Jerry Coffin Guest

    In article <g0216m$pqc$>,
    says...

    [ ... ]

    > For example, it's not undefined behaviour to do
    >
    > f(i++) + ++i;
    >
    > IIRC, but the results are unspecified.


    I believe this still gives undefined behavior. There are three sequence
    points in this expression: before f is called, when f returns, and at
    the end of the expression. The compiler can generate code that evaluates
    the pre-increment, then the post-increment, then the function call.
    There's no sequence point between the pre- and post-increment, so the
    result is undefined behavior.

    > And if your class has the
    > operators ++() or ++(int) overloaded, the results are even well-
    > defined and predictable. For 'int' they aren't...


    Well-defined but not necessarily predictable. The fact that it's a
    function call imposes a sequence point as the function is called, and as
    it returns -- but still doesn't require that the functions be called in
    any particular order, so the value as the function is entered can be
    unpredictable. For example:

    #include <iostream>

    class X {
    int value;
    public:
    X() : value(0) {}
    X(int v) : value(v) {}
    X &operator++() {
    ++value;
    return *this;
    }
    X operator+(X const &other) {
    return X(value + other.value);
    }
    friend std::eek:stream &operator<<(std::eek:stream &os, X const &x) {
    return os << x.value;
    }
    };

    int main() {
    X x, left, right;
    std::cout << (left= ++x) + (right = ++x) << "\n";
    std::cout << "Left = " << left << "\n";
    std::cout << "Right = " << right << "\n";
    return 0;
    }

    Now, this gives defined behavior -- regardless of the order of
    evaluation, there is always a sequence point between executing the two
    increment operators. Nonetheless, the compiler is free to produce code
    that evaluates either the left or the right sub-expression first, so the
    values of 'left' and 'right' aren't entirely predictable.

    For example, using Comeau, I get Left = 1 and Right = 2. Using G++ or
    Microsoft, I get Left = 2 and Right = 1.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, May 10, 2008
    #5
  6. bintom

    Jerry Coffin Guest

    In article <d16e4f6a-c4f2-4b6f-bf7d-
    >,
    says...
    > On 9 mai, 19:21, "Victor Bazarov" <> wrote:


    [ ... ]

    > > For example, it's not undefined behaviour to do

    >
    > > f(i++) + ++i;

    >
    > Yes it is, since there is no sequence point between the two
    > incrementations. Sequence points only define a partial
    > ordering: there is a sequence point before calling f, but that
    > only establishes and ordering between i++ and the call to f; it
    > doesn't establish any ordering between the two incrementations.


    I believe it establishes _some_ ordering, but not enough[1]. In
    particular, I believe the compiler is required to treat evaluation of a
    function argument and calling the function as atomic -- i.e. once the
    evaluation of any argument takes place, it must proceed to evaluated the
    other arguments (if any) and then call the function.

    In the expression above, I don't believe it's allowed for the post-
    increment to be evaluated, then the pre-increment, then the function
    call. It is, however, allowed for the pre-increment, then the post-
    increment, then the function call -- and in this ordering, there is no
    sequence point between the pre-increment and the post-increment, so the
    result is undefined behavior.

    1: Though it's open to some question. $1.9/8 says: "Once the execution
    of a function begins, no expressions from the calling function are
    evaluated until execution of the called function has completed."

    I'm interpreting evaluating the arguments to a function as part of
    execution of the function. If you choose to interpret it as a completely
    separate act that happens before the function's execution, then you're
    right -- no ordering is defined. At least in this case, it doesn't make
    any real difference though -- the overall result is undefined behavior
    either way.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, May 10, 2008
    #6
  7. bintom

    Bo Persson Guest

    Jerry Coffin wrote:
    > In article <d16e4f6a-c4f2-4b6f-bf7d-
    > >,
    > says...
    >> On 9 mai, 19:21, "Victor Bazarov" <> wrote:

    >
    > [ ... ]
    >
    >>> For example, it's not undefined behaviour to do

    >>
    >>> f(i++) + ++i;

    >>
    >> Yes it is, since there is no sequence point between the two
    >> incrementations. Sequence points only define a partial
    >> ordering: there is a sequence point before calling f, but that
    >> only establishes and ordering between i++ and the call to f; it
    >> doesn't establish any ordering between the two incrementations.

    >
    > I believe it establishes _some_ ordering, but not enough[1]. In
    > particular, I believe the compiler is required to treat evaluation
    > of a function argument and calling the function as atomic -- i.e.
    > once the evaluation of any argument takes place, it must proceed to
    > evaluated the other arguments (if any) and then call the function.
    >
    > In the expression above, I don't believe it's allowed for the post-
    > increment to be evaluated, then the pre-increment, then the function
    > call. It is, however, allowed for the pre-increment, then the post-
    > increment, then the function call -- and in this ordering, there is
    > no sequence point between the pre-increment and the post-increment,
    > so the result is undefined behavior.
    >
    > 1: Though it's open to some question. $1.9/8 says: "Once the
    > execution of a function begins, no expressions from the calling
    > function are evaluated until execution of the called function has
    > completed."
    >
    > I'm interpreting evaluating the arguments to a function as part of
    > execution of the function. If you choose to interpret it as a
    > completely separate act that happens before the function's
    > execution, then you're right -- no ordering is defined. At least in
    > this case, it doesn't make any real difference though -- the
    > overall result is undefined behavior either way.


    I think your second interpretation is the correct one. It means that
    ++i can be evalueated before or after calling f, but not during.

    However, evaluating the arguments is done before calling the function,
    not as part of the call.

    Breaking it down:

    evaluating i++
    sequence point
    call function
    return
    sequence point
    add


    At the point of the add operation, the result of the right hand side
    of the addition must be available. It must be evaluated somewhere
    before the add operation, obviously, but not between the two sequence
    points.



    Bo Persson
    Bo Persson, May 11, 2008
    #7
  8. bintom

    Old Wolf Guest

    On May 11, 3:01 am, Jerry Coffin <> wrote:
    > > On 9 mai, 19:21, "Victor Bazarov" <> wrote:
    > > > For example, it's not undefined behaviour to do
    > > > > f(i++) + ++i;

    >
    > I believe the compiler is required to treat evaluation of a
    > function argument and calling the function as atomic -- i.e. once the
    > evaluation of any argument takes place, it must proceed to evaluated the
    > other arguments (if any) and then call the function.


    There is no such requirement. In the following code,
    for example,:
    foo( bar(), baz() ) + qux();

    the order of calling functions could be:
    baz, qux, bar, foo.

    or any other ordering, so long as 'foo' comes after
    both 'bar' and 'baz'.

    The code of Victor Bazarov causes undefined behaviour
    if 'i' is a builtin type, because there might not be
    a sequence point between the two modifications of 'i'.
    Old Wolf, May 11, 2008
    #8
  9. bintom

    Jerry Coffin Guest

    In article <>, says...

    [ ... ]

    > > 1: Though it's open to some question. $1.9/8 says: "Once the
    > > execution of a function begins, no expressions from the calling
    > > function are evaluated until execution of the called function has
    > > completed."
    > >
    > > I'm interpreting evaluating the arguments to a function as part of
    > > execution of the function. If you choose to interpret it as a
    > > completely separate act that happens before the function's
    > > execution, then you're right -- no ordering is defined. At least in
    > > this case, it doesn't make any real difference though -- the
    > > overall result is undefined behavior either way.

    >
    > I think your second interpretation is the correct one. It means that
    > ++i can be evalueated before or after calling f, but not during.


    You could _certainly_ be right. As I said, in this case it doesn't
    really make much difference, since either one gives undefined results.

    I got curious and read through N2284, and found that the wording has
    been changed to [intro.execution]/16:

    Every evaluation in the calling function (including other
    function calls) that is not otherwise specifically
    sequenced before or after the execution of the body of
    the called function is indeterminately sequenced with
    respect to the execution of the called function.

    This is apparently an attempt at clarifying the situation, but doesn't
    seem (to me) to add much clarity. In particular, it specifically cites
    execution of the body of the function in one place, but execution of the
    called function in the other. It's not at all clear whether this is just
    mildly sloppy wording, and the two are intended to be synonymous, or
    whether it's completely intentional, and intended to delineate between
    the two.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, May 11, 2008
    #9
  10. bintom

    James Kanze Guest

    On 10 mai, 17:01, Jerry Coffin <> wrote:
    > In article <d16e4f6a-c4f2-4b6f-bf7d-
    > >,
    > says...


    > > On 9 mai, 19:21, "Victor Bazarov" <> wrote:


    > [ ... ]


    > > > For example, it's not undefined behaviour to do


    > > > f(i++) + ++i;


    > > Yes it is, since there is no sequence point between the two
    > > incrementations. Sequence points only define a partial
    > > ordering: there is a sequence point before calling f, but
    > > that only establishes and ordering between i++ and the call
    > > to f; it doesn't establish any ordering between the two
    > > incrementations.


    > I believe it establishes _some_ ordering, but not enough[1].
    > In particular, I believe the compiler is required to treat
    > evaluation of a function argument and calling the function as
    > atomic -- i.e. once the evaluation of any argument takes
    > place, it must proceed to evaluated the other arguments (if
    > any) and then call the function.


    Where do you get that? I've used compilers which violate it,
    and I'm fairly sure that it is neither required by the standard,
    nor was it the intent. There was even a DR for C, the answer of
    which made it quite clear that the only reordering which isn't
    allowed is intervening the actual execution of the function with
    other parts of the expression.

    > In the expression above, I don't believe it's allowed for the
    > post- increment to be evaluated, then the pre-increment, then
    > the function call. It is, however, allowed for the
    > pre-increment, then the post- increment, then the function
    > call -- and in this ordering, there is no sequence point
    > between the pre-increment and the post-increment, so the
    > result is undefined behavior.


    > 1: Though it's open to some question. $1.9/8 says: "Once the
    > execution of a function begins, no expressions from the
    > calling function are evaluated until execution of the called
    > function has completed."


    > I'm interpreting evaluating the arguments to a function as
    > part of execution of the function.


    That's the first time I've heard that interpretation. The
    classical example of undefined behavior in C is:
    f( i++ ) + f( i ++ )

    > If you choose to interpret it as a completely separate act
    > that happens before the function's execution, then you're
    > right -- no ordering is defined. At least in this case, it
    > doesn't make any real difference though -- the overall result
    > is undefined behavior either way.


    In practice, I'd say that if you have to ask the question, the
    code is probably doing something it shouldn't anyway:). For
    readability's sake, even if it is defined. But the classical
    interpretation, at least within the C committee (many years ago)
    was that the execution of the function is what happens between
    the sequence point entering the function, and the sequence point
    leaving it.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, May 11, 2008
    #10
  11. bintom

    James Kanze Guest

    On 11 mai, 17:05, Jerry Coffin <> wrote:
    > In article <>, says...


    > [ ... ]


    > > > 1: Though it's open to some question. $1.9/8 says: "Once
    > > > the execution of a function begins, no expressions from
    > > > the calling function are evaluated until execution of the
    > > > called function has completed."


    > > > I'm interpreting evaluating the arguments to a function as
    > > > part of execution of the function. If you choose to
    > > > interpret it as a completely separate act that happens
    > > > before the function's execution, then you're right -- no
    > > > ordering is defined. At least in this case, it doesn't
    > > > make any real difference though -- the overall result is
    > > > undefined behavior either way.


    > > I think your second interpretation is the correct one. It
    > > means that ++i can be evalueated before or after calling f,
    > > but not during.


    > You could _certainly_ be right. As I said, in this case it
    > doesn't really make much difference, since either one gives
    > undefined results.


    > I got curious and read through N2284, and found that the
    > wording has been changed to [intro.execution]/16:


    > Every evaluation in the calling function (including other
    > function calls) that is not otherwise specifically
    > sequenced before or after the execution of the body of
    > the called function is indeterminately sequenced with
    > respect to the execution of the called function.


    > This is apparently an attempt at clarifying the situation, but
    > doesn't seem (to me) to add much clarity.


    This isn't so much an attempt at clarifying the situation, as at
    making the text relevant in a multithreaded environment.

    I'm pretty sure that there was a DR in C which made this clear;
    the actual question was different (could the compiler interleaf
    the execution of two functions), but the text in the
    clarification made it clear that just about any reordering is
    permitted before the actual function call and after the return.
    (This is from memory, however; I have no idea how to go about
    finding the actual DR, and it's quite possible that I'm basing
    my opinion on discussions concerning the DR, and not only on the
    formal answer to it.)

    > In particular, it specifically cites execution of the body of
    > the function in one place, but execution of the called
    > function in the other. It's not at all clear whether this is
    > just mildly sloppy wording, and the two are intended to be
    > synonymous, or whether it's completely intentional, and
    > intended to delineate between the two.


    In general, I think that there is a fairly widespread consensus
    that the way C describes sequence points, and the way they
    establish an ordering, is far from optimal. The C committee
    decided to not address the issue in the C99 revision, on the
    grounds that while far from optimal, it was sufficient, and
    there was no real indication that any of the proposed
    alternatives were better (i.e. clearer). Unless you want to
    specify ordering fully, as in Java, it's a difficult issue to
    define precisely when to stop. The philosophy of sequence
    points doesn't work at all in a multithreaded environment,
    however, so the C++ committee is obliged to find something else.
    Hopefully, clearer, but unless you define full ordering, it's
    difficult to be really clear (and requiring full ordering in a
    multithreaded environment has a number of additional
    problems---even Java punts on this one, resulting in what would
    be called in C++ "undefined behavior").

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, May 11, 2008
    #11
    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. Replies:
    104
    Views:
    10,992
    Jordan Abel
    Oct 28, 2005
  2. Replies:
    99
    Views:
    2,491
    eliza81
    Jun 11, 2010
  3. bintom
    Replies:
    9
    Views:
    337
    James Kanze
    May 10, 2008
  4. Alf P. Steinbach /Usenet
    Replies:
    0
    Views:
    889
    Alf P. Steinbach /Usenet
    May 22, 2011
  5. I H H
    Replies:
    6
    Views:
    127
    Peter Wyzl
    Nov 12, 2004
Loading...

Share This Page