C Test Incorrectly Uses printf() - Please Confirm

M

Martin O'Brien

I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I
think has the wrong answer. The question is this:

What is printed:

int a = 1;
printf("%d", ++a, a + 5);

a. 1
b. 2
c. 7
d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.

The first expression in the parameter list following the format
string is paired with the first (and only) conversion specification.
The increment is a prefix so the result of the operation is the a + 1.
Since there are more items in the value list than there are conversion
specifications, the extra value is not shown.

I believe the correct answer is d because according to K&R2 (and by
implication the Standard) the order in which function arguments are
evaluated is not defined; in fact, K&R2's example, in Section 2.12,
shows the variable n being used twice in the same printf call (albeit
with the correct number of conversion specifications).

Am I correct that d is the correct answer?
 
M

Malcolm McLean

   int a = 1;
   printf("%d", ++a, a + 5);

   a. 1
   b. 2
   c. 7
   d. undefined
Am I correct that d is the correct answer?
I'm pretty sure you're right. But the important thing is it doesn't
really matter. It's not clear what the value of a + 5 should be, or is
intended by the programmer, so the construct is unusable. It may be
implementation-defined rather than undefined, but that leads us into
inappropriate thumbing through the footnotes of the standard, and is
something only a compiler writer should do.
 
S

Shao Miller

Martin said:
I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I
think has the wrong answer. The question is this:

What is printed:

int a = 1;
printf("%d", ++a, a + 5);

a. 1
b. 2
c. 7
d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.
What about (b)? There is a sequence point before the function 'printf'
is called, so '++a' has finished being evaluated and has modified the
value of 'a'. 'a + 5' is also evaluated, but the format string doesn't
use that argument. 'a + 5' does not attempt to modify the value of 'a'.
 
S

Shao Miller

Shao said:
What about (b)? There is a sequence point before the function 'printf'
is called, so '++a' has finished being evaluated and has modified the
value of 'a'. 'a + 5' is also evaluated, but the format string doesn't
use that argument. 'a + 5' does not attempt to modify the value of 'a'.
As in, the second argument has a definite value at least by the time of
the function call. So does the third argument have a value. It doesn't
matter whether the third argument's value is 6 or 7, because the format
string doesn't use it. Thus '2' is printed.

If the question instead asked, "What will be the value passed as the
third argument?" I would guess (d). But the order in which these
arguments are evaluated cannot impact what value is passed as the second
argument; the only one to be printed.
 
B

bert

I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I
think has the wrong answer. The question is this:

   What is printed:

   int a = 1;
   printf("%d", ++a, a + 5);

   a. 1
   b. 2
   c. 7
   d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.

   The first expression in the parameter list following the format
string is paired with the first (and only) conversion specification.
The increment is a prefix so the result of the operation is the a + 1.
Since there are more items in the value list than there are conversion
specifications, the extra value is not shown.

I believe the correct answer is d because according to K&R2 (and by
implication the Standard) the order in which function arguments are
evaluated is not defined; in fact, K&R2's example, in Section 2.12,
shows the variable n being used twice in the same printf call (albeit
with the correct number of conversion specifications).

Am I correct that d is the correct answer?

No, the correct answer is (b). Answer (d)
would apply where the printf arguments were
the other way round.

The argument "++a" evaluates to 2, whether
it is evaluated before "a + 5" or after it.

The argument "a + 5" is likely to evaluate
to either 6 or 7, depending on whether it is
evaluated before or after "++a". The standard
may well allow its evaluation to be undefined,
because of the ++a (I'm no standards expert)
but as the argument isn't printed, it doesn't
matter.

However: that printf format string does not
have a new line character, so the output
will not appear at once; and there have
been implementations of printf which
assumed that their parameter count matched
the number of fields in the format string,
and caused a stack fault if it didn't; in
which case the answer would be (e) nothing.
--
 
E

Ersek, Laszlo

I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I think
has the wrong answer. The question is this:

What is printed:

int a = 1;
printf("%d", ++a, a + 5);

a. 1
b. 2
c. 7
d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.

The first expression in the parameter list following the format string
is paired with the first (and only) conversion specification. The
increment is a prefix so the result of the operation is the a + 1. Since
there are more items in the value list than there are conversion
specifications, the extra value is not shown.

I believe the correct answer is d because according to K&R2 (and by
implication the Standard) the order in which function arguments are
evaluated is not defined; in fact, K&R2's example, in Section 2.12,
shows the variable n being used twice in the same printf call (albeit
with the correct number of conversion specifications).

Am I correct that d is the correct answer?

Yes, but I believe not for the reason you mention.

C99 6.5.2.2 Function calls, p10:

"The order of evaluation of the function designator, the actual arguments,
and subexpressions within the actual arguments is unspecified, but there
is a sequence point before the actual call."

So the order of evaluation is unspecified, not undefined. However,

C99 6.5 Expressions, p2:

"Between the previous and next sequence point an object shall have its
stored value modified at most once by the evaluation of an expression.
Furthermore, the prior value shall be read only to determine the value to
be stored."

Here are the sequence points:
int a = 1;

SP#1 at semicolon.
printf(
"%d", ++a, a + 5

SP#2 after "printf" (the function designator) and all arguments have been
evaluated (in unspecified order), right before printf() is entered.

SP#3 at semicolon.

"a" is read twice and written once between SP#1 and SP#2. The expression
"a+5" reads "a" *not* in order to determine "a"'s next value, so 6.5p2 is
violated. Hence the undefined behavior.

(In practice, compilers need 6.5p2 so that they can reliably order
store/load operations between adjacent sequence points along data
dependency edges. If no such dependency is found, they can choose whatever
order. They can even overlap/interleave such simple expressions as ++a and
a+5 -- not function calls though.)

Under this aspect, it is irrelevant that the value of a+5 will be "thrown
away" anyway. C's eval is eager (see 6.5.2.2p10 above, for example), not
lazy.

lacos
 
E

Eric Sosman

I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I
think has the wrong answer. The question is this:

What is printed:

int a = 1;
printf("%d", ++a, a + 5);

a. 1
b. 2
c. 7
d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.

You were right.
The first expression in the parameter list following the format
string is paired with the first (and only) conversion specification.
The increment is a prefix so the result of the operation is the a + 1.
Since there are more items in the value list than there are conversion
specifications, the extra value is not shown.

The explanation is wrong. The undefined behavior arises from the
lack of a sequence point between ++a and a+5, before printf() even
begins to execute. It doesn't matter that the value (if any) of a+5
won't be used; the behavior is undefined anyhow.

Ponder this one:

printf ("%d\n", ++a, strlen(NULL));

The fact that the printf() will not use its third parameter does not
mean that the third argument's undefined behavior is forgiven.
I believe the correct answer is d because according to K&R2 (and by
implication the Standard) the order in which function arguments are
evaluated is not defined; in fact, K&R2's example, in Section 2.12,
shows the variable n being used twice in the same printf call (albeit
with the correct number of conversion specifications).

Am I correct that d is the correct answer?

Yes.
 
W

Willem

Shao Miller wrote:
) As in, the second argument has a definite value at least by the time of
) the function call. So does the third argument have a value. It doesn't
) matter whether the third argument's value is 6 or 7, because the format
) string doesn't use it. Thus '2' is printed.

It's undefined behaviour. Anything can happen.
'2' is not *guaranteed*.

I agree, it's extremely unlikely to be anything else.
Probably no system exists where it would not print 2.
But pedantically speaking, the result *is* undefined.

But in some theoretical machine, it could fail.
(For example, the instructions for reading in a+5 and writing in a++
could be parallelized into the same slot, and the simultaneous read
and write could cause some kind of signal to be raised.)


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
K

Keith Thompson

Malcolm McLean said:
I'm pretty sure you're right. But the important thing is it doesn't
really matter. It's not clear what the value of a + 5 should be, or is
intended by the programmer, so the construct is unusable. It may be
implementation-defined rather than undefined, but that leads us into
inappropriate thumbing through the footnotes of the standard, and is
something only a compiler writer should do.

The standard is not intended only for compiler writers. It is, in
effect, a contract between implementers and programmers; as such,
both implementers and programmers can benefit from being familiar
with it.

In practice, the fact that the presented code is ugly and should
never pass a code review, whether its behavior is defined or not,
may be more important than the reasons why its behavior is undefined.
But I'd be interested in knowing why you think that reading the
standard (and no, footnotes aren't relevant here) is "something
only a compiler writer should do.".
 
S

Shao Miller

Yes, but I believe not for the reason you mention.

C99 6.5.2.2 Function calls, p10:

"The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call."

So the order of evaluation is unspecified, not undefined. However,

C99 6.5 Expressions, p2:

"Between the previous and next sequence point an object shall have its
stored value modified at most once by the evaluation of an expression.
Furthermore, the prior value shall be read only to determine the value
to be stored."

Here are the sequence points:


SP#1 at semicolon.


SP#2 after "printf" (the function designator) and all arguments have
been evaluated (in unspecified order), right before printf() is entered.


SP#3 at semicolon.

"a" is read twice and written once between SP#1 and SP#2. The expression
"a+5" reads "a" *not* in order to determine "a"'s next value, so 6.5p2
is violated. Hence the undefined behavior.

(In practice, compilers need 6.5p2 so that they can reliably order
store/load operations between adjacent sequence points along data
dependency edges. If no such dependency is found, they can choose
whatever order. They can even overlap/interleave such simple expressions
as ++a and a+5 -- not function calls though.)

Under this aspect, it is irrelevant that the value of a+5 will be
"thrown away" anyway. C's eval is eager (see 6.5.2.2p10 above, for
example), not lazy.
Whoa. It never occurred to me that:

int i = 1;
i = i + i;

could yield undefined behaviour due to the double-read and the single
store. Amazing! Nice one! Instead, we should use:

int i = 1;
i += i;

Because it differs from:

int i = 1;
i = i + i;

In that with the similarity between 'E1 += E2' and 'E1 = E1 + E2' in the
latter case, 'E1' is only evaluated once. But uh oh... What about 'E2'?

I suppose the only well-behaved way to do it is:

int i = 1, j;
j = i;
i = i + j;

Is that right?
 
S

Shao Miller

Willem said:
Shao Miller wrote:
) As in, the second argument has a definite value at least by the time of
) the function call. So does the third argument have a value. It doesn't
) matter whether the third argument's value is 6 or 7, because the format
) string doesn't use it. Thus '2' is printed.

It's undefined behaviour. Anything can happen.
'2' is not *guaranteed*.

I agree, it's extremely unlikely to be anything else.
Probably no system exists where it would not print 2.
But pedantically speaking, the result *is* undefined.

But in some theoretical machine, it could fail.
(For example, the instructions for reading in a+5 and writing in a++
could be parallelized into the same slot, and the simultaneous read
and write could cause some kind of signal to be raised.)
What makes it undefined behaviour, if you please?

Is it also undefined behaviour for:

int i = 1;
i = i + i;

?
 
E

Eric Sosman

What makes it undefined behaviour, if you please?

`++a, a+5' with no sequence point to separate them. 6.5p2:

"Between the previous and next sequence point an object
shall have its stored value modified at most once by the
evaluation of an expression. Furthermore, the prior value
shall be read only to determine the value to be stored."

.... with special attention to the second sentence. `++a' reads the
value once to determine the value stored, and `a+5' reads the value
for a different purpose. U.B.
Is it also undefined behaviour for:

int i = 1;
i = i + i;

No. The Standard says "shall be read," not "shall be read
exactly once."
 
S

Shao Miller

Eric said:
Ponder this one:

printf ("%d\n", ++a, strlen(NULL));

The fact that the printf() will not use its third parameter does not
mean that the third argument's undefined behavior is forgiven.
I'm not sure I'd agree to pondering this example for similarity to the
first. There are two additional sequence points here; one before the
call to 'strlen' and one immediate before 'strlen' returns. Thus there
is more opportunity for undefined behaviour than in the original. Would
you agree?

5.1.2.3,p2 (of 'n1256.pdf'):

"...At certain specified points in the execution sequence called
sequence points, all side effects of previous evaluations shall be
complete and no side effects of subsequent evaluations shall have taken
place."
 
E

Eric Sosman

I'm not sure I'd agree to pondering this example for similarity to the
first. There are two additional sequence points here; one before the
call to 'strlen' and one immediate before 'strlen' returns. Thus there
is more opportunity for undefined behaviour than in the original. Would
you agree?

(Shrug.) If that worries you, consider

printf ("%d\n", ++a, *(char*)NULL);
 
S

Shao Miller

Eric said:
`++a, a+5' with no sequence point to separate them. 6.5p2:

"Between the previous and next sequence point an object
shall have its stored value modified at most once by the
evaluation of an expression. Furthermore, the prior value
shall be read only to determine the value to be stored."

... with special attention to the second sentence. `++a' reads the
value once to determine the value stored, and `a+5' reads the value
for a different purpose. U.B.
Thanks, Eric. :)
No. The Standard says "shall be read," not "shall be read
exactly once."
I think I understand. So in:

int i = 1, j;
i = j = i + i * 9 + 3;

we are likewise protected because we can conclude that sooner or later
within some expression, we are modifying the stored value of 'i'. Is
that right?
 
B

Barry Schwarz

I took an online C test a few months ago. I actually thought the test
was better than some I've taken, but one question in particular I
think has the wrong answer. The question is this:

What is printed:

int a = 1;
printf("%d", ++a, a + 5);

a. 1
b. 2
c. 7
d. undefined

I selected d. This is the explanation given as to why b is the correct
answer.

The first expression in the parameter list following the format
string is paired with the first (and only) conversion specification.
The increment is a prefix so the result of the operation is the a + 1.
Since there are more items in the value list than there are conversion
specifications, the extra value is not shown.

I believe the correct answer is d because according to K&R2 (and by
implication the Standard) the order in which function arguments are
evaluated is not defined; in fact, K&R2's example, in Section 2.12,
shows the variable n being used twice in the same printf call (albeit
with the correct number of conversion specifications).

Am I correct that d is the correct answer?

I think so, due to violating 6.5-2.

There is a sequence point prior to the start of the printf statement.
The next sequence pint is when printf is called. Between the two, the
arguments are evaluated. As a result of evaluating the second
argument, the value stored in object a is modified, we just don't know
exactly when. Evaluating the third argument reads the value of object
a for a purpose other than determining what the new value should be.

The fact that the third argument is never used probably eliminates the
practical impact of the undefined behavior. (I wonder if there are
any systems other than the DS9000 which would fail to do the
expected.) But that does not change the fact that a shall constraint
has been violated.
 
S

Shao Miller

Barry said:
I think so, due to violating 6.5-2. I disagree.

There is a sequence point prior to the start of the printf statement.
The next sequence pint is when printf is called. Between the two, the
arguments are evaluated. Agreed.

As a result of evaluating the second
argument, the value stored in object a is modified, we just don't know
exactly when.
I disagree. 5.1.2.3,p2:

"...At certain specified points in the execution sequence called
sequence points, all side effects of previous evaluations shall be
complete and no side effects of subsequent evaluations shall have taken
place."

The object designated by 'a' has its stored value modified not before
the sequence point just before calling the function. The side effects
are effectively coalesced at that point in time, by this definition.
Evaluating the third argument reads the value of object
a for a purpose other than determining what the new value should be.
The argument list in a function call is a comma-separated list of
expressions to be evaluated, not a single expression (the syntax of
6.5.2,p1 and the semantics of 6.5.2.2,p3). 6.5,p2 states "an
expression" rather than "an expression or a comma-separated list of
expressions".
The fact that the third argument is never used probably eliminates the
practical impact of the undefined behavior.
I do not agree that there is no undefined behaviour. I believe that the
correct answer is (b).
(I wonder if there are
any systems other than the DS9000 which would fail to do the
expected.) But that does not change the fact that a shall constraint
has been violated.
The value of 'a' is stable between its initializing declaration and the
instant before 'printf' is called. No single expression attempts to
modify the value and read the value for some other purpose. Would you
agree?
 
S

Shao Miller

Shao said:
I do not agree that there is no undefined behaviour. I believe that the
correct answer is (b).
Please forgive the typo. That should have been typed as:

I do not agree that there is undefined behaviour. I believe that the
correct answer is (b).
 
S

Shao Miller

pete said:
Shao said:
Ersek, Laszlo wrote:
Whoa. It never occurred to me that:

int i = 1;
i = i + i;

could yield undefined behaviour

That's good that it didn't occur to you.

The part that Ersek, Laszlo wrote which I didn't snip,
applies to that situation.

Here's the footnote that goes with it.

N869
60)This paragraph renders undefined statement expressions
such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;

Yes, a good (albeit, non-normative) footnote to think about.

i = ++i + 1;

At the sequence point, we have two modification accesses pending for 'i'
at the same time. Oops! The value of 'i' is guaranteed intact until
that sequence point.

a[i++] = i;

Is defined to be equivalent to:

(*((a) + (i++))) = i;

At the sequence point, we have a modification access pending for the
object designated by '(*((a) + (i++)))' and we have a modification
access pending for 'i' at the same time. Both of these accesses are
uses of the value of 'i' in a single expression. Since only one of the
modification accesses is for 'i', this could be considered as the prior
value being read for more than just the purpose of determining the value
to be stored in 'i'.

i = i + 1;

At the sequence point, we have a modification access pending for 'i' and
this is a use of the prior value of 'i'. Eric has suggested that more
than one read of 'i' is allowed, which would satisfy 'i = i + i;'.

a = i;

Is defined to be equivalent to:

(*((a) + (i))) = i;

At the sequence point, we have a modification access pending for the
object designated by '(*((a) + (i)))'. There is no modification access
pending for 'i', so we can read the value of 'i' as many times as we like.

So how about here?:

int i = 1, j;
i = j = i + i + 1;

At the sequence point just before the second semi-colon, we have a
modification access pending for 'i' and a modification access pending
for 'j'. Is the value of 'i' read for more than one purpose here?

Thanks, pete. :)
 

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,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top