Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > C Test Incorrectly Uses printf() - Please Confirm

Reply
Thread Tools

C Test Incorrectly Uses printf() - Please Confirm

 
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
pete wrote:
> Martin O'Brien wrote:
>> 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;
> it doesn't matter whether or not the undefined value
> of the extra expression is shown.
> It's the evaluation of the extra expression
> which makes the program be undefined.
>

Some other people have agreed to this, too. Will someone please explain
why 'a + 5' has an undefined value? The value of 'a' is guaranteed
intact between the last sequence point and the sequence point before the
call to 'printf'. Furthermore, no expression in the list of arguments
attempts to simultaneous modify 'a' and read 'a' for some purpose
unrelated to the modification.

'++a' and 'a + 5' are separate expressions.

The value of 'a' is intact until the sequence point. 'a' is not
'volatile', since we can see its declaration.

How about reading 'n1256.pdf', 6.5.3.1,p2:

"The expression ++E is equivalent to (E+=1)."

Then on to 6.5.16,p4:

"...If an attempt is made to modify the result of an assignment operator
or to access it after the next sequence point, the behavior is undefined.
"

It says "after" there, not "before".
 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
christian.bau wrote:
> On Aug 8, 11:17 pm, Shao Miller <(E-Mail Removed)> wrote:
>> Shao Miller wrote:
>>> 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).

>
> Absolutely wrong.

Please read the post which the inner post was a response to. Please
address deficiencies in interpretation of referenced text therein.

>
> There is a sequence point between the end of the preceeding statement
> and the printf statement.

Agreed.

> After that sequence point, the arguments to
> the printf call and the printf function specifier are evaluated.

Agreed.

> Then
> comes another sequence point, followed by the actual function call.

Agreed.

> The problem is that during the argument evaluation the object "a" is
> modified,

I disagree. The object's value is intact between the previous sequence
point and the one before the function call. See the other post for
references.

> and it is accessed without using the accessed value to
> determine the new value

I disagree. '++a' is defined as equivalent to '(a+=1)'. '(a+=1)'
differs from 'a=a+1' in that the value of 'a' is only read once. The
accessed value is used to determine the new value. See the prefix
increment operator definitions for details. See the compound assignment
definitions for details.

> (a parameter "a = a + 1" would be Ok, because
> the access is used to determine the new value).

'++a' is very nearly identical to 'a = a + 1', with the difference noted
just above.

> This combination of
> modification and access leads to undefined behaviour, as the C
> Standard expressly says.

Which combination, exactly? In which section does it expressly say that,
if you please?

> Once we have undefined behaviour,

Well do we? Which access leads to the undefined behaviour, exactly?

> we need not bother looking at which
> function is called and what that function does.

If we agreed to undefined behaviour, I'd agree with this.

> So what could go
> wrong? According to the rules of the C language the compiler can
> _assume_ that you don't do anything that leads to undefined behaviour.
> In this case, the compiler can _assume_ that a is not modified,

The side effect of modification does not occur until just before the
sequence point for the 'printf' call. The compiler can assume that with
confidence. Would you agree?

> even
> though it is perfectly clear that a _is_ modified.

Agreed. Before 'printf' is called, the value of 'a' is modified.

> So an optimiser can
> for example remove the code that updates the memory location
> containing a. So a second printf statement printf ("%d\n", a); could
> again print the original value 1.

The modification of the value of 'a' should not be skipped past sequence
points in a conforming implementation. Would you agree?
 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
pete wrote:
> Shao Miller wrote:
>> pete wrote:
>>> Martin O'Brien wrote:

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

>
>> Will someone please explain
>> why 'a + 5' has an undefined value?

>
> What is the value of (a + 5) ?
>

6 for a conforming implementation. 6 or 7 for a non-confrming one. In
a conforming implementation, the value of 'a' (which is non-'volatile')
is not changed until the sequence point immediately before the function
call to 'printf'.

No evaluation of any of the multiple expressions in the argument list
leads to undefined behaviour, so the integrity of the abstract machine's
operation in also intact. Would you agree that the expressions in the
argument list are not a single expression (although it might look like
an expression with multiple comma operators, for example)?

Thanks, pete.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-09-2010
Shao Miller <(E-Mail Removed)> writes:
> pete wrote:
>> Shao Miller wrote:
>>> pete wrote:
>>>> Martin O'Brien wrote:

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

>>
>>> Will someone please explain
>>> why 'a + 5' has an undefined value?

>>
>> What is the value of (a + 5) ?
>>

> 6 for a conforming implementation. 6 or 7 for a non-confrming one. In
> a conforming implementation, the value of 'a' (which is non-'volatile')
> is not changed until the sequence point immediately before the function
> call to 'printf'.


Where does the standard say that the side effect of incrementing a must
not occur until the sequence point? (Hint: It doesn't.)

> No evaluation of any of the multiple expressions in the argument list
> leads to undefined behaviour, so the integrity of the abstract machine's
> operation in also intact. Would you agree that the expressions in the
> argument list are not a single expression (although it might look like
> an expression with multiple comma operators, for example)?


The expression in question is the entire function call.
By themselves, the subexpressions ++a and a + 5 are well defined.
But because they appear together as part of a larger expression
with no intervening sequence points, the behavior is undefined.

Once again, 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.

The printf violates the second sentence; the second subexpression a +
5 reads the value of a, but not to determine the value to be stored.

That second sentence can be confusing (it confused me for a long
time). The point is that if an object is read just to determine
the value to store in that same object, there's no problem, since
the read logically has to occur before the write. But if the object
is read in a part of the expression that doesn't contribute to the
computation of the value to be stored, then the read and the write
could occur in either order, at the whim of the compiler.

The authors of the standard *could* have placed some limitations
on how such expressions are evaluated, saying, for example, that
either ++a must be evaluted (including its side effect) before a +
5, or vice versa. But that would constrain the optimizations that
compilers are able to perform and make the standard more complex.
And it's generally easy enough to avoid writing such code in the
first place.

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      08-09-2010
On 8/8/2010 6:14 PM, Shao Miller wrote:
> [...]
> "...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.


No, the object designated `a' *definitely* has its stored value
modified before the sequence point that precedes the function call.
Have you confused "before" and "after," or are you smoking something?
In any event, it makes no never-mind: The damage is done before any
sequence point intervenes to prevent it, and that's all she wrote.

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


Yes, but the Standard speaks of "sequence points," not of syntactic
forms. There is no sequence point associated with the , that divides
argument expressions from each other. (Do not confuse the , separator
with the , operator.)

>> 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 think you've mis-counted the parity of your negatives, because
your first sentence (as written) contradicts the second (as written).

>> (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?


No, absolutely not. With the two arguments `++a, a+5', the value
of `a' is *un*stable, unstable to the point where (in theory) it need
not even exist.

--
Eric Sosman
(E-Mail Removed)lid
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      08-09-2010
On 8/8/2010 8:27 PM, Shao Miller wrote:
> pete wrote:
>> Shao Miller wrote:
>>> pete wrote:
>>>> Martin O'Brien wrote:

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

>>
>>> Will someone please explain
>>> why 'a + 5' has an undefined value?

>>
>> What is the value of (a + 5) ?
>>

> 6 for a conforming implementation. 6 or 7 for a non-confrming one. In a
> conforming implementation, the value of 'a' (which is non-'volatile') is
> not changed until the sequence point immediately before the function
> call to 'printf'.


No. The Standard localizes side-effects to the intervals[*] between
sequence points, but no more tightly. The side-effect can occur at any
moment between the preceding and succeeding SP's, or may even be spread
out over a lengthy span between them. There is no requirement that all
side-effects occur at the "barrier" of the succeeding SP, no more than
there is a requirement for them all to occur just after the "admitter"
of the preceding SP.
[*] Note that the "net" of sequence points need not be linear, so
the notion of "interval" pertains only to a particular traversal. In
the situation at hand there is only one branch, but other situations
may be more intricate.

> No evaluation of [... fantasy snipped ...]


--
Eric Sosman
(E-Mail Removed)lid
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      08-09-2010
On 8/8/2010 5:39 PM, Shao Miller wrote:
> Eric Sosman wrote:
>> On 8/8/2010 4:47 PM, Shao Miller wrote:
>>> [...]
>>> 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."
>>

> 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?


Sorry; I do not see what you're driving at.

--
Eric Sosman
(E-Mail Removed)lid
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
Keith Thompson wrote:
> Shao Miller <(E-Mail Removed)> writes:
>> pete wrote:
>>> Shao Miller wrote:
>>>> pete wrote:
>>>>> Martin O'Brien wrote:
>>>>>> int a = 1;
>>>>>> printf("%d", ++a, a + 5);
>>>> Will someone please explain
>>>> why 'a + 5' has an undefined value?
>>> What is the value of (a + 5) ?
>>>

>> 6 for a conforming implementation. 6 or 7 for a non-confrming one. In
>> a conforming implementation, the value of 'a' (which is non-'volatile')
>> is not changed until the sequence point immediately before the function
>> call to 'printf'.

>
> Where does the standard say that the side effect of incrementing a must
> not occur until the sequence point? (Hint: It doesn't.)

Thanks for pointing this out, Keith! This is a critical flaw to my
argument.

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

This does not imply that side effects are prohibited between sequence
points and only completed as a sequence point is passed.
>
>> No evaluation of any of the multiple expressions in the argument list
>> leads to undefined behaviour, so the integrity of the abstract machine's
>> operation in also intact. Would you agree that the expressions in the
>> argument list are not a single expression (although it might look like
>> an expression with multiple comma operators, for example)?

>
> The expression in question is the entire function call.
> By themselves, the subexpressions ++a and a + 5 are well defined.
> But because they appear together as part of a larger expression
> with no intervening sequence points, the behavior is undefined.
>

This is another critical flaw to my argument. Treating the entirety of
'printf("%d", ++a, a + 5)' as an expression qualifies it as a single
expression for the treatment of:

"Between the previous and next sequence point an object shall have its
stored value modified at most once by the evaluation of an expression..."

This particular argument you offer requires that:

For any expression, if a side effect during evaluation of that
expression would modify the value of an object, then the previous value
of that object shall be read only as part of a computation for the new
value of the object.

In fact, that's nearly what the text states, and why we could expect:

int i = 1, b;
b = ++i + i;

to be undefined. But please also recognize the possible implication of
"only". Does this prevent a larger expression with the same sequence
point boundaries from doing anything else but a modification of the
object? This larger expression can have other side effects.

So an interpretation could be that the value cannot be used outside of
the sub-expression which directly provides the value to be stored. Then
the latter line of:

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

would be grouped as:

i = (j = (i + i));

and we are fine in that 'i' is only read within the sub-expression
immediately responsible for the new value. We should not:

int i, j;
/* Uh oh */
j = (i + (i = (1)));

because there's a read of 'i' outside of the the sub-expression for the
new value. Would you agree?

int i = 1, j = 1, k = 1;
/* Fine */
k = ((j = ((i = (i + j + k)) + j + k)) + k);

int i = 0, j;
int *ip = &i;
/* Uh oh, 'i' reads outside of the sub-expression */
*(ip + i) = (i + 5);
*(ip + (j = i)) = (i + 5);

Would you agree?

> Once again, 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.
>
> The printf violates the second sentence; the second subexpression a +
> 5 reads the value of a, but not to determine the value to be stored.
>
> That second sentence can be confusing (it confused me for a long
> time). The point is that if an object is read just to determine
> the value to store in that same object, there's no problem, since
> the read logically has to occur before the write. But if the object
> is read in a part of the expression that doesn't contribute to the
> computation of the value to be stored, then the read and the write
> could occur in either order, at the whim of the compiler.

Agreed. lacos and Willem explained how simultaneous reads and writes
could be a problem.

>
> The authors of the standard *could* have placed some limitations
> on how such expressions are evaluated, saying, for example, that
> either ++a must be evaluted (including its side effect) before a +
> 5, or vice versa. But that would constrain the optimizations that
> compilers are able to perform and make the standard more complex.

That would be a Bad Thing.

> And it's generally easy enough to avoid writing such code in the
> first place.
>

Agreed. I appreciate your explanatory efforts here, Keith.
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
Eric Sosman wrote:
> On 8/8/2010 6:14 PM, Shao Miller wrote:
>> [...]
>> "...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.

>
> No, the object designated `a' *definitely* has its stored value
> modified before the sequence point that precedes the function call.
> Have you confused "before" and "after,"

Thanks, Eric. No. That particular argument was based on the faulty
assumption that side effects were coalesced at sequence point boundaries
and that evaluations must be based on the set of values yielded by the
last sequence point before any side effects are possible. This can
hardly be justified, given:

"Accessing a volatile object, modifying an object, modifying a file, or
calling a function that does any of those operations are all side
effects, which are changes in the state of the execution environment.
Evaluation of an expression may produce side effects..."

I apologize for the confusion.

> or are you smoking something?
> In any event, it makes no never-mind: The damage is done before any
> sequence point intervenes to prevent it, and that's all she wrote.
>

Ok.

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

>
> Yes, but the Standard speaks of "sequence points," not of syntactic
> forms. There is no sequence point associated with the , that divides
> argument expressions from each other. (Do not confuse the , separator
> with the , operator.)
>

I'd already recommended that, so definitely agree that the two should
not be confused.

The trick here is in treating those sub-expressions as part of a larger
expression to which 6.5,p2 applies, and not each on their own. If
each sub-expression were mandated to execute in _a_ sequence (rather
than in parallel), then though we mightn't know what 'a + 5' gets, it
would get _something_. That something would then be discarded in the
'printf'. There doesn't appear to be such a mandate, does there?

>>> 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 think you've mis-counted the parity of your negatives, because
> your first sentence (as written) contradicts the second (as written).
>

I've noticed that some posters give responses to posts where they are
potentially disadvantaged by not having read more than the one which
they wish to reply to. You think I've mis-counted, I think I corrected
it long before your response. No sweat.

>>> (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?

>
> No, absolutely not. With the two arguments `++a, a+5', the value
> of `a' is *un*stable, unstable to the point where (in theory) it need
> not even exist.
>

Ok. Thanks again, Eric. Seems pretty convincing for answer (d) now,
I'll happily admit. Heh.
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      08-09-2010
Eric Sosman wrote:
> On 8/8/2010 5:39 PM, Shao Miller wrote:
>> Eric Sosman wrote:
>>> On 8/8/2010 4:47 PM, Shao Miller wrote:
>>>> [...]
>>>> 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."
>>>

>> 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?

>
> Sorry; I do not see what you're driving at.
>

And I am sorry for not explaining with the detail you require.

What I was trying to ask was if 'i' in the last code line given above is
used only to determine the value to be stored in 'i'. It seems that a
compiler could make that determination by noting that within that
expression, 'i' is modified.

That is, it needn't be the case that both of "'i' is read only to
determine the value to be stored in 'i'" and "'i' is not read to
determine the value to be stored in any other object" are defined.

As in, we can assign to 'j' and even though that's a side effect of its
own, the reading of 'i' is still part of a larger computation with
responsibility for determining the new value for 'i'.

Does that make sense?
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
C Test Incorrectly Uses printf() - Please Confirm Martin C Programming 4 08-10-2010 05:25 PM
Confirm my Performance Test Against Java? Ben Christensen Ruby 39 09-03-2009 05:20 AM
Please: ITemplate propery being saved incorrectly -- Attributes usage incorrect? S.Sigal ASP .Net Building Controls 0 08-14-2004 02:07 PM
Please: ITemplate propery being saved incorrectly -- Attributes usage incorrect? S.Sigal ASP .Net Web Controls 0 08-14-2004 02:04 PM
test test test test test test test Computer Support 2 07-02-2003 06:02 PM



Advertisments