i++ * i++

K

Keith Thompson

Keith Thompson said:
Macro expansion is just textual replacement. It works on tokens, not
on expressions.

Consider this program, which demonstrates that Douglas Adams was
right, even without using base 13:
================================
#include <stdio.h>

#define SIX 1+5
#define NINE 8+1

int main(void)
{
printf("%d * %d = %d\n", SIX, NINE, SIX * NINE);
return 0;
}
================================

Which is why, in a macro definition, each reference to each argument
should normally be parenthesized, as should the entire expansion.

For example, this:

#define SUM(x, y) x + y

is potentially very dangerous; it should be:

#define SUM(x, y) ((x) + (y))
 
A

arnold

arnold said:
Just to make sure I understand you correctly, given the following

func1(int x) { return(x * x); }

i=3;
func1(i++); -> return(3 * 3) => 9
func1(++i); -> return(5 * 5) => 25

So the compiler can optimise, reorganise how ever it likes but the
logical effect of it is as described above, correct?

A couple of follow up questions, I was reading the faq on expressions
and some questions came to mind about it.

What you are saying about undefined behaviour is that, if f.ex. an i++
or ++i is used twice in an expression it causes undefined behaviour,
such as by now the famous
i++ * i++
but if its
i++ * j++
then its defined

in question 3.3 it is stated

"neither i++ nor ++i is the same as i+1. If you want to increment i, use
i=i+1, i+=1, i++, or ++i, not some combination."

I am not sure I understand it, is that what I said above, dont use a
variable twice in an expression?

question 3.4

"Operator precedence and explicit parentheses impose only a partial
ordering on the evaluation of an expression."
I dont quite understand this, If I write

((3 + 4) * 2)

in my mind, the paretheses causes the following evaluation

(3 + 2) => 5 then
(5 * 2) => 10

because the second statement can not be evaluated before the first is
evaluated. So does the fact contradict this assumption?

arnold
 
K

Keith Thompson

arnold said:
A couple of follow up questions, I was reading the faq on expressions
and some questions came to mind about it.

What you are saying about undefined behaviour is that, if f.ex. an i++
or ++i is used twice in an expression it causes undefined behaviour,
such as by now the famous
i++ * i++
but if its
i++ * j++
then its defined

Right. Note that, in some more complex cases, it can be difficult to
determine whether an expression exhibits undefined behavior just by
looking at it. For example:

arr++ * arr[j]++

is ok if i != j, but if they're equal, it exhibits undefined behavior
(because it modifies an object, namely arr, twice).
in question 3.3 it is stated

"neither i++ nor ++i is the same as i+1. If you want to increment i,
use i=i+1, i+=1, i++, or ++i, not some combination."

I am not sure I understand it, is that what I said above, dont use a
variable twice in an expression?

Using a variable twice in an expression is ok. *Modifying* a variable
twice in an expression is not. More precisely, modifying a variable
twice without an intervening sequence point invokes undefined
behavior. (Many expressions don't have sequence points within them.)
question 3.4

"Operator precedence and explicit parentheses impose only a partial
ordering on the evaluation of an expression."
I dont quite understand this, If I write

((3 + 4) * 2)

in my mind, the paretheses causes the following evaluation

(3 + 2) => 5 then
(5 * 2) => 10

because the second statement can not be evaluated before the first is
evaluated. So does the fact contradict this assumption?

Given ((3 + 4) * 2), the compiler is free to replace the entire
expression with a literal 14, so the order of evaluation isn't
visible.

But consider the case where the operands are function calls rather than
integer constants:

((f3() + f4()) * f2())

The addition cannot be performed until after both f3() and f4() have
been called, because until then there's nothing for it to add.
Likewise, the multiplication cannot be performed until all three
functions have been called.

*But*, there is no defined ordering on the function calls themselves.
Regardless of operator precedence and parentheses, the function calls
could occur in any order:

f3(), f4(), f2()
f3(), f2(), f4()
f4(), f3(), f2()
f4(), f2(), f3()
f2(), f3(), f4()
f2(), f4(), f3()

The compiler can generate code to call all three functions in any
arbitrary order, save the results, and then perform the addition and
multiplication. Precedence and parentheses only control which
operators take which operands.

This can be significant if the functions have side effects (such as
output).

The "+" and "*" operators themselves have no side effects, but
consider the case of passing function call results to functions.
Replacing "+" by add() and "*" by mult():

mult(add(f3(), f4()), f2())

Here we have 5 function calls, but not all orderings are allowed,
since a function can't be called until its arguments are available.
The ordering constraints are:

f3() and f4() must be called before add()
add() and f2() must be called before mult()

This reduces the 120 (5 factorial) possible orderings to some smaller
number that I'm too lazy to figure out.

And I'm sure that's a *lot* more than you wanted to know.
 
C

Charles Richmond

Lew said:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


Nope. Because your code modifies an object twice between sequence
points, you've strayed into the realm of "undefined behaviour", and
/any/ answer is the "proper" answer.
IMHO it is too bad that the C standard can *not* specify that
when you modiy an object twice between sequence points...
a hand will come out of the screen and bitch-slap you.
 
R

Richard Bos

arnold said:
ok, see. so logically, within one expression, that would mean

use value of i, in the next expression containing i use i+1

No. For _correct_ code, it could be expressed somewhat inaccurately as
something like

use value of i, starting with the next _full_ expression containing i
use i+1

However...
let me check if I understand, an expression of the form

(((i_1++ - b) / 3) + i_2++) + c-i_3)

would then mean

i_1 expression: use value of i_1,
i_2 expression: use value of i_1+1,
i_3 expression: use value of i_1+2

i_1, i_2 and i_3 are not full expressions, they are sub-expressions of
the whole line.
Actually, "full expression" is misleading. The real sticking point is
"between sequence points". The end of a statement is a sequence point,
but so are some select operators such as the logical (&&, ||) and comma
operators, and the actual call of a function, _after_ all its operands
have been determined, and some others. The * operator, however, is not,
and neither is any of the operators in your line above.

The real meaning of i++ is

evaluate to the value of i, and as a side effect, increment i

and that of ++i is

evaluate to the value of i plus one, and as a side effect, increment i

In neither of these cases is there any guarantee about the order in
which the the evaluation and the side effect are performed; and all side
effects _may_ be done just after the previous sequence point (statement
start, logical operator) and they _may_ be done just before the next
sequence point (end of the statement, function call), or they may be
done as a left-to-right reading of the code would indicate, or they may
even be shoved off to a parallel processor and done at the same time as
the main value of the expression is calculated. Or worse.
But since the OP's expression uses macro it al changes and leads to
undefined behaviour.

The macro _as such_ has nothing to do with it. The problem is that
_this_ macro, applied as it is, expands to

i++ * i++

and _that_ expression causes the undefined behaviour. It would do so if
it were entered straight into the code just as much as when it comes
from a macro.

Richard
 
R

Ravi Nakidi

Hi Rohit,


Compiler always executing the program sequentially. But, macros having
some side effects. I think this is already known by u very well.
Without compiler permission it will change the values of the variable.

See how,

Compiler start execution from int i=3, on

then it comes to the j = PRODUCT(i++);

then this is as it is submitted to the macro then u'r macro became

#define PRODUCT(i++) (i++ * i++);

now i = 3, so 3++ * 3++
so 3*3 =9 now j became 9 but i will become 5 correct becuse i++ and i++
in macro;

Then it comes to the second k = PRODUCT(++i);

this is ++i * ++i correct i is already 4 then pre increament ++i then
i=6 and then ++i the i=7

then 7*7 =49

Correct.


Regards,
Ravi Nakidi,
S/w Engg
 
A

arnold

Keith said:
Given ((3 + 4) * 2), the compiler is free to replace the entire
expression with a literal 14, so the order of evaluation isn't
visible.

I understand. (So you know, my example was simplified as imagine an
expression where variables had the following values, i could have used
variable names, but for some reason I did not.
And I'm sure that's a *lot* more than you wanted to know.

No, it was exactly the explanation I was looking for.

arnold
 
C

Chris Smith

Ravi Nakidi said:
Compiler start execution from int i=3, on

then it comes to the j = PRODUCT(i++);

then this is as it is submitted to the macro then u'r macro became

#define PRODUCT(i++) (i++ * i++);

now i = 3, so 3++ * 3++
so 3*3 =9 now j became 9 but i will become 5 correct becuse i++ and i++
in macro;

Then it comes to the second k = PRODUCT(++i);

this is ++i * ++i correct i is already 4 then pre increament ++i then
i=6 and then ++i the i=7

then 7*7 =49

That's certainly one of the infinite number of things that a conforming
compiler could generate from that code. However, it's no more correct
than the compiler generating generating an answer of -1234567890. There
are multiple assignments between sequence points. The result is
undefined. Period. That's really all there is to it.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
M

Mark McIntyre

Yes, but there are no sequence points between the evaluations of the
arguments. This:
func(i++, i++);
invokes undefined behavior because it modifies i twice between
sequence points (regardless of what happens inside func()).

You're right of course. FAQ 3.2
Mark McIntyre
 
M

Mark McIntyre

a literal replacement for each user of the paramter argument?
So, if the paramtere had been: (i++/23) then the expression would have been

(i++/23) * (i++/23)

Yes.
if you have
#define MACRO (x) (x*x)

and then
MACRO(3+4) ;
it expands to

3+4 * 3+4;
Just to make sure I understand you correctly, given the following

func1(int x) { return(x * x); }

i=3;
func1(i++); -> return(3 * 3) => 9
func1(++i); -> return(5 * 5) => 25

So the compiler can optimise, reorganise how ever it likes but the
logical effect of it is as described above, correct?

Absolutely. The 'as if' rule. The compiler can optimise it any way it
wants as long as the result of the code is exactly 'as if' it had done
no optimisation.

For instance given
x = func1(3);
the compiler could convert that into
x = 9;


Mark McIntyre
 
K

Keith Thompson

Mark McIntyre said:
You're right of course. FAQ 3.2

I thought about citing that, but it doesn't actually illustrate the
point. The statement in FAQ 3.2 is:

printf("%d\n", i++ * i++);

That would still be invalid even if there were sequence points between
argument evaluations, since both modifications of i take place within
the same argument.

In fact, I think that adding

printf("%d %d\n", i++, i++);

to the FAQ might be a good idea.
 
B

Barry Schwarz

Hi Rohit,


Compiler always executing the program sequentially. But, macros having

Compilers don't execute programs.
some side effects. I think this is already known by u very well.
Without compiler permission it will change the values of the variable.

I don't think a macro can have a side effect since it is only visible
to the preprocessor and side effects occur during execution.
See how,

Compiler start execution from int i=3, on

Compilers compile programs.
then it comes to the j = PRODUCT(i++);

Given the macro described below, the compiler will not see this
statement. It will see the statement as produced by the preprocessor
which would be
j = (i++ * i++);

When executed, this statement invokes undefined behavior. There is no
way the compiler can generate "correct" machine code since there is no
correct undefined behavior.

Everything that follows is unrelated to the C language as defined by
the standard.
then this is as it is submitted to the macro then u'r macro became

#define PRODUCT(i++) (i++ * i++);

now i = 3, so 3++ * 3++
so 3*3 =9 now j became 9 but i will become 5 correct becuse i++ and i++
in macro;

Then it comes to the second k = PRODUCT(++i);

this is ++i * ++i correct i is already 4 then pre increament ++i then
i=6 and then ++i the i=7

then 7*7 =49

Correct.


Regards,
Ravi Nakidi,
S/w Engg


Remove del for email
 
K

Kenneth Brody

Mark said:
Yes.
if you have
#define MACRO (x) (x*x)

and then
MACRO(3+4) ;
it expands to

3+4 * 3+4;

And, in case it's missed by newbies, the above is totally different from
the apparently intended use of returning the square of the parameter.
(The result here being 19, not 49. If you don't understand why, go back
to your manual and re-read "operator precedence". How to solve this is
left as an exercise to the reader.)

[...]

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:[email protected]>
 
K

Kenneth Brody

Barry said:
On 17 Mar 2006 01:23:57 -0800, "Ravi Nakidi" <[email protected]>
wrote: [...]
then it comes to the j = PRODUCT(i++);

Given the macro described below, the compiler will not see this
statement. It will see the statement as produced by the preprocessor
which would be
j = (i++ * i++);

When executed, this statement invokes undefined behavior. There is no
way the compiler can generate "correct" machine code since there is no
correct undefined behavior.

Or, one could say that anything generated is "correct" because there is
no way it could be called "incorrect".

[...]

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:[email protected]>
 
D

Dave Thompson

It compiles code for `i++ * i++` assuming that you know what you're
doing. So, at a guess, it multiples i (3) by i (still 3) to get 9.
The it increments i, possibly twice.

The it compiles code for `++i * ++i` ATYKWYD. At a guess, it increments
i, probably twice, then multiplies i (7) by i (still 7) to get 49.

The compiler-writer of course could do things differently. They could
keep a flag for each variable, "increment pending". Then `i++` compiles
as "get the value of i and set the flag", and ";" compiles as "increment
all variables with the flag set, and clear it"; and "++i" compiles as
"increment i, and return the new value". In that case, you'd
get the results 9 and 20.

Or they could compile `i++` as "if the flag is set, report a problem,
plant code to quit the program; otherwise, get the value of i and
set the flag."

Some versions (3.x up) of gcc detect this and give a warning for at
least the simplest and most obvious cases.

I don't know if they do it with a flag per symbol. In principle I
could read the source, it being free, if only I had a few decades of
spare time and a trust fund (or government grant) to live off.

- David.Thompson1 at worldnet.att.net
 

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
474,176
Messages
2,570,947
Members
47,501
Latest member
Ledmyplace

Latest Threads

Top