Postfix operators

Discussion in 'C Programming' started by BartC, Feb 7, 2014.

  1. BartC

    BartC Guest

    Are there any situations where there can be two or more postfix operators
    after a term of an expression?

    (As in: A ++ ++; but this is not valid due to type issues.)

    (If so, I was just wondering if the right-to-left evaluation rule for unary
    operators still applies, so that if a, b, c, d are unary operators, and X
    was a term, then: a b X c d would have the evaluation order d, c, b, a, or
    (a (b ((X d) c))). Notice the last two, c and d, changing positions.)
    BartC, Feb 7, 2014
    1. Advertisements

  2. BartC

    James Kuyper Guest

    There's lots of them. For example


    Most combinations of the postfix operators can work together in the
    right situation, but you're right, the following is one of the exceptions:
    For every postfix operator except primary-expressions and compound
    literals, the left operand must itself be a complete postfix-expression,
    which means that they are evaluated from left to right. Therefore, the
    expression above is equivalent to:

    x = ((((func(a)) ) .member1) ->member2) ++;
    James Kuyper, Feb 7, 2014
    1. Advertisements

  3. BartC

    BartC Guest

    OK, "()" was one I hadn't thought of. (But "->" and "." I consider to be
    binary infix operators, with left and right binding tightly.)

    () evaluates left to right and unary ops from right to left. So I guess


    means: -((X())++). I don't know about X++()++()(), but that is so unlikely
    that I will ignore it.

    (At the minute I'm just trying to fully understand terms such as *p++ which
    occur everywhere. It seems that for most terms which I'm going to come
    across (or try and translate into different syntax), I can use my own
    informal rule which is:

    - First evaluate postfix ops, in left-to-right order
    - Then evaluate prefix ops, in right-to-left order

    When there is at most one postfix right-to-left operator (++ or --), I think
    that that is more-or-less how C would treat it.)
    BartC, Feb 7, 2014
  4. BartC

    James Kuyper Guest

    The syntax of the function call, subscript, and compound literal
    operators surrounds the right operand, which is not the case for the
    member selection operators. The increment and decrement operators have
    no right operands. Nonetheless, the C standard describes them all as
    postfix operators.

    Don't forget that the unary operators have a lower precedence than the
    postfix operators. In terms of the C grammar, that is described by the
    fact a postfix expression counts as a unary-expression, but not vice-versa.
    It's a constraint violation for a postfix ++ to be applied to anything
    other than values of real type or values that point to complete object
    types. The result of a postfix ++ expression has the same type as it's
    left operand. It's a constraint violation for the function call operator
    to be applied to anything other than a pointer to a function type.
    Therefore, if the first ++ doesn't make that expression a constraint
    violation, the following () will; the same is separately true for the
    second ++() combo, though by then it hardly matters. While many
    combinations of postfix operators can work together if the situation is
    right, others do not, and that expression contains two examples of that
    That approach works for the ++ and -- operators. However, you must apply
    cast operators after the postfix operators, but before applying the
    unary operators: & * + - ~ !, because their right operand is required to
    be a cast-expression.
    That's never an issue for prefix ++ and -- operators, because they can
    only be applied to modifiable lvalues, and the result of a cast is never
    an lvalue.
    * is the only unary operator that does return an lvalue, so ++*x is
    possible, and has a very different meaning from *++x.
    James Kuyper, Feb 7, 2014

  5. Section 6.5.2 of the C standard lists the postfix operators. Actually
    it defines the syntactical term *postfix-expression*, which has several
    forms. Array indexing, function calls, member selection, postfix ++ and
    --, and compound literals are all listed.

    The section is titled "Postfix operators", but it doesn't really define
    what a "postfix operator" is. The inclusion of compound literals in
    particular is odd, because there is no postfix operator in the syntax;
    it's included in that section because it makes the syntax work. Perhaps
    "postfix operators" isn't the best term, but I can't think of a better
    one. The grammar is fairly arbitrary.

    Personally, I think of ".member" and "->member" as postfix operators,
    not as infix operators, because the thing that appears on the right hand
    side is not an expression; in "struct_name.member_name", "struct_name"
    is the operand, and ".member_name" is the operator.

    X() (a function call) does not yield an lvalue, so it's not legal to
    apply postfix (or prefix) ++ to it.
    The association of operators with operands is defined by the grammar --
    which does not, to be clear, define the order of evaluation.
    Keith Thompson, Feb 7, 2014
  6. BartC

    BartC Guest

    By 'postfix' operators I actually only meant the unary ones, in other words
    ++ and --. (There are (), [] and such but I considered those syntactic
    elements not operators.)
    A function could return a pointer to something that could be used as an
    lvalue. But it seems you need to dereference such a result, using *, to use
    it as an lvalue. This also means that:

    p ++ --

    doesn't work, and neither does *p++ -- (I think because the order is applied
    as --, ++, then *) but (*p++)-- does (because the * is applied between
    the -- and ++). Anyway I think that means I don't need to worry about
    consecutive ++ and -- ops.

    (Note: if *p++ -- really is parsed as *((p--)++), then that is a little
    crazy, because the evaluation order of ++ and -- is the reverse of what
    might be expected!)
    BartC, Feb 7, 2014
  7. BartC

    James Kuyper Guest

    On 02/07/2014 02:50 PM, BartC wrote:
    Why do you think that? The grammar specifies parsing it as

    * p ++ --
    * primary-expression ++ --
    * postfix-expression ++ --
    | |
    * postfix-expression --
    | |
    * postfix-expression
    * cast-expression
    | |

    or, in other words, as the equivalent of *((p++)--).
    James Kuyper, Feb 7, 2014
  8. BartC

    BartC Guest

    Because the operator chart on p53 of K&R2 states that *, ++ and -- are
    applied right-to-left. Or rather it says right-to-left associativity, which
    I assumed meant the same thing.

    But if ++ is applied before -- in '*p ++ --', then I admit I've no idea what
    right-to-left associativity means in this context.

    However if your parsing is correct, then unary ops are implemented pretty in
    much the same way as I implement them elsewhere.
    BartC, Feb 7, 2014
  9. BartC

    Ike Naar Guest

    That is true for prefix ++ and prefix --.
    The postfix ++ and postfix -- fall into the [] () . -> category and
    have left-to-right associativity.
    Ike Naar, Feb 7, 2014
  10. BartC

    James Kuyper Guest

    The grammar for the postfix ++ and -- operators is different from the
    prefix versions of those operators. The prefix versions are described in
    the unary operators section of the standard, along with *, so any chart
    that groups *, ++, and -- together is probably referring to the prefix
    ++ and -- operators. Those operators do associate right-to-left, though
    only with the other unary operators - it's always a constraint violation
    of some kind to have two consecutive ++ or -- operators.
    James Kuyper, Feb 7, 2014
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.