Is this well-defined?

T

Tom Zych

Hi. I've been using code like this:

*p++ = 'x';
*p++ = *q++;

for a long time now. It works. The code below compiles with no
warnings and runs correctly. Even splint doesn't complain about
those lines.

Is this well-defined according to the standard? I've read a little
about sequence points and not doing things like "i = i++". This
would seem to be similar. Is it only a problem if you're using and
modifying the *same* variable, more than once, without an
intervening sequence point?

In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

--------------------------------------------------
#include <stdio.h>

int main(void)
{
char s[] = "Some\ntext\nin\nseveral\nlines\n";
char t[100], *src, *dest;

// replace each LF with CRLF
for (src = s, dest = t; *src; ) {
if (*src == '\n')
*dest++ = '\r';
*dest++ = *src++;
}
*dest = '\0';

fputs(t, stdout);
return 0;
}
 
M

Mark A. Odell

Tom Zych said:
Hi. I've been using code like this:

*p++ = 'x';
*p++ = *q++;
Fine.

for a long time now. It works. The code below compiles with no
warnings and runs correctly. Even splint doesn't complain about
those lines.

Sure.
Is this well-defined according to the standard? I've read a little
about sequence points and not doing things like "i = i++". This
would seem to be similar. Is it only a problem if you're using and
modifying the *same* variable, more than once, without an
intervening sequence point?

In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

Yes this is well defined. The values of *p and *q must be calculated and
used for the assigment. Also, p and q must be incremented. The order this
happens is up to the compiler but the result is well-defined and is what
you expect.
 
H

Hallvard B Furuseth

Tom said:
In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

No. But who cares? Unlike i = i++, the above statement doesn't modify
the _same_ location twice. That is, unless p or q point to themselves
(cast to the appropritate type) - which would be a weird thing to do.

Your code is fine.

Well, I would have used const char src[] and const char *s, or just used
char *s = "Some\ntext\nin\nseveral\nlines\n"; and dropped src, so that
the string literal would end up in read-only memory when that is
supported. And /**/ comments instead of // comments if the code is
to be compiled on C89 compilers. But these are different issues.
 
I

Irrwahn Grausewitz

Tom Zych said:
Hi. I've been using code like this:

*p++ = 'x';
*p++ = *q++;

for a long time now. It works. The code below compiles with no
warnings and runs correctly. Even splint doesn't complain about
those lines.

Is this well-defined according to the standard? Yes.

I've read a little
about sequence points and not doing things like "i = i++". This
would seem to be similar. Is it only a problem if you're using and
modifying the *same* variable, more than once, without an
intervening sequence point? Exactly.


In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented? Yes.


--------------------------------------------------
#include <stdio.h>

int main(void)
{
char s[] = "Some\ntext\nin\nseveral\nlines\n";
char t[100], *src, *dest;

// replace each LF with CRLF
for (src = s, dest = t; *src; ) {
if (*src == '\n')
*dest++ = '\r';
*dest++ = *src++;
}
*dest = '\0';

fputs(t, stdout);
return 0;
}
--------------------------------------------------
Looks good.

Regards

Irrwahn
 
F

Fao, Sean

Tom Zych said:
Hi. I've been using code like this:

*p++ = 'x';
*p++ = *q++;

This is fine.
[...] In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

Assuming the compiler you use is conforming to the standard, yes.
 
F

Fao, Sean

Hallvard B Furuseth said:
Tom said:
In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

No. But who cares? [...]

You should care. If the assignment happened after the increment, the result
would be totally different.
 
D

Dan Pop

In said:
Hi. I've been using code like this:

*p++ = 'x';
*p++ = *q++;

for a long time now. It works. The code below compiles with no
warnings and runs correctly. Even splint doesn't complain about
those lines.

Is this well-defined according to the standard? I've read a little
about sequence points and not doing things like "i = i++". This
would seem to be similar.

If it seems similar to you, then you've understood nothing from what
you've read.
Is it only a problem if you're using and
modifying the *same* variable, more than once, without an
intervening sequence point?

In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

The answer is obvious once you really understand what *p++ means. Things
are easier if we rewrite it as *(p++). So p++ gets evaluated first,
yielding the old value of p as its result. Therefore, it is this value
that becomes the argument of the * operator, regardless of the actual
moment when p gets incremented. The same discussion applies to *q++.

So, even if we don't know when exactly p and q get actually incremented
(sometime before the next sequence point), the expression has well
defined behaviour (assuming p and q are properly initialised).

Dan
 
H

Hallvard B Furuseth

Hallvard B Furuseth said:
Tom said:
In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

No. But who cares? [...]

You should care. If the assignment happened after the increment, the
result would be totally different.

No. Do you think that if q was assinged before *p, *p would be set to
q[1] instead of q[0]? Not so. The value of q++ is the original q, no
matter when the actual modification happens.

The C standard does not define in which order these modifications
happens. That's the entire point. That's why i = i++ is undefined.
 
M

Mark A. Odell

In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

No. But who cares? Unlike i = i++, the above statement doesn't modify
the _same_ location twice. That is, unless p or q point to themselves
(cast to the appropritate type) - which would be a weird thing to do.

Your code is fine.

Well, I would have used const char src[] and const char *s, or just used
char *s = "Some\ntext\nin\nseveral\nlines\n"; and dropped src, so that
the string literal would end up in read-only memory

There is no such thing as read-only memory in C. Implementation may put
such things in FLASH or protected address ranges but you should never
depend on this behavior. The 'const' keyword is only a *compile-time*
check, not a run-time check.
 
H

Hallvard B Furuseth

Mark said:
There is no such thing as read-only memory in C.

I know. That's why my next phrase, which you snipped, was 'when that is
supported'. The way the OP's code was written, the string could not be
putin read-only memory even when that was supported.
 
M

Mark A. Odell

I know. That's why my next phrase, which you snipped, was 'when that is
supported'. The way the OP's code was written, the string could not be
putin read-only memory even when that was supported.

My point still stands, why would one ever take advantage of something you
can depend on (unless you are doing embedded systems, like me).
 
B

Ben Pfaff

Fao said:
Tom Zych said:
[...] In particular, is

*p++ = *q++;

guaranteed to dereference p and q, and perform the assignment,
before either is incremented?

Assuming the compiler you use is conforming to the standard, yes.

No. The compiler is allowed to do the increments at any time
during the expression evaluation. But it is required to
dereference the unincremented addresses that p and q pointed to.

That is, one possible implementation is something like this:

T *old_p = p;
T *old_q = q;
p++;
q++;
*old_p = *old_q;

Note that the increments occur before the dereferences.
 
H

Hallvard B Furuseth

Mark said:
My point still stands, why would one ever take advantage of something you
can

I assume there should be a "not" here, otherwise I really don't know
what you mean...
depend on (unless you are doing embedded systems, like me).

This is ridiculous. You might as well say there is no point in
distributing systems with Makefiles or whatever that turn on
optimization, because not all compilers optimize well.

Anyway, I'm not depending on read-only memory. Nor taking advantage of
it. I'm allowing the _compiler_ to take advantage of it. If it can't
or won't, that's all right - no harm is done. If it does, the user has
gained several advantages at no cost, like a little extra error
checking, a and little faster startup and less memory usage.
 
M

Mark A. Odell

I assume there should be a "not" here, otherwise I really don't know
what you mean...

I did mean 'not' there. Sorry.

This is ridiculous. You might as well say there is no point in
distributing systems with Makefiles or whatever that turn on
optimization, because not all compilers optimize well.

Anyway, I'm not depending on read-only memory. Nor taking advantage of
it. I'm allowing the _compiler_ to take advantage of it. If it can't
or won't, that's all right - no harm is done. If it does, the user has
gained several advantages at no cost, like a little extra error
checking, a and little faster startup and less memory usage.

It's not rediculous and your analogy is poor. Your second paragraph goes
on to further make my point. You yourself don't depend on the feature as a
programmer. I have no trouble with a compiler placing const things into
FLASH or what have you but I, as a programmer, am not going to depend on
that read-onlyness to protect my program against bad user/sensor input at
run-time. This was my point.
 
H

Hallvard B Furuseth

Mark said:
It's not rediculous and your analogy is poor. Your second paragraph
goes on to further make my point. You yourself don't depend on the
feature as a programmer. I have no trouble with a compiler placing
const things into FLASH or what have you but I, as a programmer, am
not going to depend on that read-onlyness to protect my program
against bad user/sensor input at run-time. This was my point.

Talk about poor analogies. I already said I don't depend on it, and the
case where I suggested const don't depend on it. And it doesn't catch
user errors like your example, it catches programmer errors in the same
program. If you never make mistakes in your programs, this won't matter
to you. If you ever do make mistakes, it's good to get used to using
techniques that help catch errors, in particular when all it costs is
that you have to write 'const ' twice - like in this case.
 
M

Malcolm

Tom Zych said:
*p++ = 'x';
*p++ = *q++;

for a long time now. It works.
This is perfectly OK and idiomatic C. The only problems will occur if p
points to q, in which case I think the behaviour is undefined, but it
doesn't really matter because it creates a horrible and unfollowable
expression.

i = i++ and similar are also nonsense expressions from the point of view of
anyone reading the code. A good compiler should warn about them even if they
do happen to be technically legal.
 
P

Peter Nilsson

Malcolm said:
This is perfectly OK and idiomatic C. The only problems will occur if p
points to q, in which case I think the behaviour is undefined, but it
doesn't really matter because it creates a horrible and unfollowable
expression.

It's a common idiom, so I don't know why you'd describe it as a
'horrible and unfollowable expression'. But more importantly, if p
where to point to q, then the incompatibility between types would
require a diagnostic. If you meant p would point to the same object
that q points to, then the line would be inefficient, but nonetheless
well defined (like a = a;).
i = i++ and similar are also nonsense expressions
from the point of view of anyone reading the code.
A good compiler should warn about them even if they
do happen to be technically legal.

Possibly, but a good programmer wouldn't let themselves become
dependant on good compilers. :)
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top