Can somebody explain to me the output of the following code?

A

asdfghjk

#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41
 
C

Chris H

In message <[email protected]
s.com> said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


See and click at the bottom to se the answers. It is a bit old now but
it should answer your questions

http://www.phaedsys.demon.co.uk/chris/sweng/swengtips3.htm
 
M

Mark Bluemel

asdfghjk said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.


Not surprisingly, as I'm sure that was the originator's intention.

Here's how I understand the line "x->a = x->b, ++x++->b;"

We have two expressions separated by a "," (remember that an assignment
is itself an expression). The expressions are evaluated left to right,
and the rightmost is the value of the expression as a whole - which in
this case (and probably most cases) we ignore.

The first expression is "x->a = x->b" which is fairly self-explanatory.
So the first time through the loop, the value 1 is replaced by the value 10.

The second expression "++x++->b" does far too many things, IMHO. The
pre-increment operates on the integer "x->b" (turning 10 to 11), while
the post-increment operates on x, moving it on in step with the indexing
in the loop. The value returned by the expression (which is again
ignored) is the incremented value of "x->b".
 
M

Mark Bluemel

Chris said:
In message <[email protected]
s.com> said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


See and click at the bottom to se the answers. It is a bit old now but
it should answer your questions

http://www.phaedsys.demon.co.uk/chris/sweng/swengtips3.htm


Hmmm... I'm not convinced

That page claims that
<Quote>
uint16_t i = 10;
uint16_t j = 0;

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

The code is effectively running the following calculation - "I=10, j=3,
I * j++"

</Quote>

Firstly, I don't see why "uint16_t" is used in preference to plain
"int", especially since "%d" in printf requires int...

Secondly, I'm fairly sure that the explanation makes some major
assumptions about the order of evaluation of function arguments, which
aren't necessarily true.

Am I mistaken?
 
B

Ben Bacarisse

Chris H said:

int main(void) is better. Given that main returns an int a return 0; is
a good idea too.
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;

To the OP, a hint. It helps if you know how this is parsed. All
postfix operators bind more tightly than all prefix operators so this is

++( (x++)->b )

Does that help?
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


See and click at the bottom to se the answers. It is a bit old now but
it should answer your questions

http://www.phaedsys.demon.co.uk/chris/sweng/swengtips3.htm


I don't see the connection between that page and the OP's code. All
your examples are undefined (though you never say that for some reason)
but I think the OP's code is well-defined (though ghastly).
 
B

Ben Bacarisse

Mark Bluemel said:
Chris said:
In message <[email protected]
s.com> said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


See and click at the bottom to se the answers. It is a bit old now but
it should answer your questions

http://www.phaedsys.demon.co.uk/chris/sweng/swengtips3.htm


Hmmm... I'm not convinced

That page claims that
<Quote>
uint16_t i = 10;
uint16_t j = 0;

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

The code is effectively running the following calculation - "I=10,
j=3, I * j++"

</Quote>

Firstly, I don't see why "uint16_t" is used in preference to plain
"int", especially since "%d" in printf requires int...


That will be fine on many systems because uint16_t will promote to int
when passed to printf but it does seem odd that, after including
inttypes.h, the correct format was not used (PRIu16).

As you say, for illustration, int would be much simpler.
Secondly, I'm fairly sure that the explanation makes some major
assumptions about the order of evaluation of function arguments, which
aren't necessarily true.

Am I mistaken?

I think all the examples are "potentially" undefined (I may have missed
a defined one) in that they all might read the prior value of j (for a
purpose other than determining the new value for j) without an
intervening sequence point.

I say "potentially" undefined because I suppose that if the change in j
happens before any other "read" of j, the prior value is not being read,
only the new one is. I'd still say "undefined" as a genaral answer
since from the C point of view you can't be sure to avoid it.
 
S

Shao Miller

Hmmm... I'm not convinced

That page claims that
<Quote>
uint16_t i = 10;
uint16_t j = 0;

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

The code is effectively running the following calculation - "I=10, j=3,
I * j++"

</Quote>

Firstly, I don't see why "uint16_t" is used in preference to plain
"int", especially since "%d" in printf requires int...

Secondly, I'm fairly sure that the explanation makes some major
assumptions about the order of evaluation of function arguments, which
aren't necessarily true.

Am I mistaken?
I think the matter of 'printf' with the 'j++' was recently covered in
the "C Test Incorrectly Uses printf() - Please Confirm" thread.
Modification of 'j' but a read of 'j' in another argument could be
considered to be a violation of C99's section 6.5, paragraph 2;
leading to undefined behaviour. A compiler could refuse to translate
the source code. At run-time, some extremely unexpected results are
allowed of a conforming implementation.
 
B

Ben Bacarisse

I think all the examples are "potentially" undefined (I may have missed
a defined one) in that they all might read the prior value of j (for a
purpose other than determining the new value for j) without an
intervening sequence point.

I say "potentially" undefined because I suppose that if the change in j
happens before any other "read" of j, the prior value is not being read,
only the new one is. I'd still say "undefined" as a genaral answer
since from the C point of view you can't be sure to avoid it.

I take this last "potentially" bit back and revert to my original reply
to Chris H. It is, at best, a sort of C sophistry at worst plain wrong.
The code is undefined.

I think any reference to the object that is changing will constitute a
read of the "prior value" from the C standard point of view even if an
implementation ensures that the change has, in practise, happened. Only
at the next sequence point does the prior value change to being "the
value" of the object.
 
A

Andrey Tarasevich

Mark said:
Hmmm... I'm not convinced

That page claims that
<Quote>
uint16_t i = 10;
uint16_t j = 0;

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

The code is effectively running the following calculation - "I=10, j=3,
I * j++"

</Quote>

Firstly, I don't see why "uint16_t" is used in preference to plain
"int", especially since "%d" in printf requires int...

Secondly, I'm fairly sure that the explanation makes some major
assumptions about the order of evaluation of function arguments, which
aren't necessarily true.

The above `printf` produces undefined behavior. Making assumptions about
its order of evaluation makes no sense whatsoever.
 
A

Andrey Tarasevich

asdfghjk said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


The output of this code cannot be meaningfully explained from the point
of view of C language. The `++x++` subexpression produces Undefined
Behavior, meaning that even if this code generates some output, it can
be safely perceived as essentially random.

In fact, the code is not even guaranteed to compile, since the compilers
are allowed to refuse to compile code with UB.
 
A

Andrey Tarasevich

Andrey said:
asdfghjk said:
#include <stdio.h>
main()
{
struct Data
{
int a;
int b;
} y[4] = { 1, 10, 3, 30, 2, 20, 4, 40};
struct Data *x = y;
int i;
for(i=0; i<4; i++)
{
x->a = x->b, ++x++->b;
printf("%d %d\t", y.a, y.b);
}
}

I have confusion regarding the ++x++->b part.
The output is : 10 11 30 31 20 21 40 41


The output of this code cannot be meaningfully explained from the point
of view of C language. The `++x++` subexpression produces Undefined
Behavior, meaning that even if this code generates some output, it can
be safely perceived as essentially random.

In fact, the code is not even guaranteed to compile, since the compilers
are allowed to refuse to compile code with UB.


Scratch that. What I said above is incorrect. I misparsed the `++x++->b`
subexpression.
 
A

Andrey Tarasevich

Chris said:
See and click at the bottom to se the answers. It is a bit old now but
it should answer your questions

http://www.phaedsys.demon.co.uk/chris/sweng/swengtips3.htm

Old or not, that page makes a major mistake of ignoring the critical
qualitative difference between the Undefined Behavior and Unspecified
Behavior in C. It takes a bunch of situations where the behavior is
_undefined_ and proceeds to treat them as if the behavior is
_unspecified_. While it might "work" in some (or many) cases in
practice, the approach is nevertheless flawed and makes very little
sense (or no sense whatsoever).
 
N

Nick

Andrey Tarasevich said:
The above `printf` produces undefined behavior. Making assumptions
about its order of evaluation makes no sense whatsoever.

I don't think it produces official undefined behaviour. It could,
however, give you:
10, 3, 20

I think.
 
A

Andrey Tarasevich

Nick said:
I don't think it produces official undefined behaviour. It could,
however, give you:
10, 3, 20

Yes, it does produce official UB, no debate about it.

Variable `j` is modified in `i * j++` and also read for a completely
independent reason in `j`. This is a direct and straightforward
violation if requirements of 6.5/2. I.e. there's an evaluation scenario
in which these two events are not separated by a sequence point, meaning
that the behavior us undefined.
 
A

Andrey Tarasevich

Andrey said:
Yes, it does produce official UB, no debate about it.

Variable `j` is modified in `i * j++` and also read for a completely
independent reason in `j`. This is a direct and straightforward
violation if requirements of 6.5/2. I.e. there's an evaluation scenario
in which these two events are not separated by a sequence point, meaning
that the behavior us undefined.

BTW, a very similar issue was discussed on SO recently. You can refer to

http://stackoverflow.com/questions/3450582/c-programming-is-this-undefined-behavior/3451334#3451334

for more details.
 
N

Nick

Andrey Tarasevich said:
Yes, it does produce official UB, no debate about it.

Variable `j` is modified in `i * j++` and also read for a completely
independent reason in `j`. This is a direct and straightforward
violation if requirements of 6.5/2. I.e. there's an evaluation
scenario in which these two events are not separated by a sequence
point, meaning that the behavior us undefined.

Yeah, you're right. I never write such horrible code, so I've never had
to fix in my mind exactly when a comma is a sequence point and exactly
when it isn't.
 
S

Seebs

I don't think it produces official undefined behaviour.

It does.

There's no sequence point between the access to j and the modification
of j in "j++". Therefore the behavior is undefined.

-s
 
K

Keith Thompson

Nick said:
Yeah, you're right. I never write such horrible code, so I've never had
to fix in my mind exactly when a comma is a sequence point and exactly
when it isn't.

A comma is a sequence point only when it's a comma operator.
(A comma operator evaluates its left argument and discards the
result, then evaluates its right arguments and yields its results.)

The commas separating arguments in function calls are not operators;
they're just part of the syntax of a function call.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top