Postfix operators

B

BartC

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.)
 
J

James Kuyper

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

There's lots of them. For example

func(a).member1->member2++

Most combinations of the postfix operators can work together in the
right situation, but you're right, the following is one of the exceptions:
(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.)

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) ++;
 
B

BartC

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

There's lots of them. For example

func(a).member1->member2++


OK, "()" was one I hadn't thought of. (But "->" and "." I consider to be
binary infix operators, with left and right binding tightly.)
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) ++;


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

-X()++

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.)
 
J

James Kuyper

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

There's lots of them. For example

func(a).member1->member2++


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


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.
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) ++;


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


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.
-X()++

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

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
fact.
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

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.
 
K

Keith Thompson

BartC said:
James Kuyper said:
Are there any situations where there can be two or more postfix operators
after a term of an expression?

There's lots of them. For example

func(a).member1->member2++


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


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.
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) ++;


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

-X()++

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


X() (a function call) does not yield an lvalue, so it's not legal to
apply postfix (or prefix) ++ to 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.)

The association of operators with operands is defined by the grammar --
which does not, to be clear, define the order of evaluation.
 
B

BartC

Keith Thompson said:
BartC said:
James Kuyper said:
On 02/07/2014 06:07 AM, BartC wrote:
Are there any situations where there can be two or more postfix
operators
after a term of an expression?

There's lots of them. For example

func(a).member1->member2++


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


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.


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.)
X() (a function call) does not yield an lvalue, so it's not legal to
apply postfix (or prefix) ++ to it.

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!)
 
J

James Kuyper

On 02/07/2014 02:50 PM, BartC wrote:
....
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 *)

Why do you think that? The grammar specifies parsing it as


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

or, in other words, as the equivalent of *((p++)--).
 
B

BartC

James Kuyper said:
On 02/07/2014 02:50 PM, BartC wrote:
...

Why do you think that? The grammar specifies parsing it as

or, in other words, as the equivalent of *((p++)--).

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.
 
I

Ike Naar

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.

That is true for prefix ++ and prefix --.
The postfix ++ and postfix -- fall into the [] () . -> category and
have left-to-right associativity.
 
J

James Kuyper

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.

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.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top