Strange - a simple assignment statement shows error in VC++ but worksin gcc !

R

Rainer Weikusat

It might communicate that to you, but it wouldn't communicate it to
me.

That's not a matter of 'you' and 'me', but a matter of the defined
meaning of the text in question. That I may happen to know about this
meaning, but not about some random, weird convention you happen to be
accustomed to, while you claim to have no idea of the defined meaning,
but obviously know about the random weird convention is completely
besides the point, in exactly the same way the colour of my eyes or
the number of past boy-friends of your daughter would be.

Please consider moving the discussion about 'personals and
personalities' into a talk show or some other region of the world
reserved for useless, antagonizing talk about 'others'.
 
D

Doctor Smith

After takin' a swig o' grog, David Schwartz belched out
this bit o' wisdom:


And you will be able to find more bugs or loose code in any project,
even after many passes over it.

You're out of your league LiarMutt.......
 
D

David Schwartz

It communicates that someone wrote (unsigned) for some reason and that,
taking the semantics of C into account, this was a reason of a
non-technical nature, ie a magic incantation intended placate some
Cerberus on the way to elysium, such as a code reviewer known to
enforce random, aesthetic preferences of his own.

Bluntly, I have been doing this for years and that has never happened
as far as I know. The reverse has happened many, many times, and
continues to happen on a regular basis.

DS
 
D

David Schwartz

And you will be able to find more bugs or loose code in any project,
even after many passes over it.

True that, but not six bugs in an hour. At least, not usually. Of
course, this is just one example, so it's not exactly scientifically
convincing evidence.

I continued for another hour and did find one more serious bug. This
one was interesting. The code used to look like this:

int i;
for(i=0; i<GetMaxValue(foo); i++)
{
/* code goes here */
}
/* lots of code here */
for(i=GetMaxValue(foo); i>=0; i--)
{
/* code goes here */
}

But then someone noticed that 'GetMaxValue' returned an unsigned and
decided to change the type of 'i' to 'unsigned' to match, not noticing
the lower loop. I guess they assumed that 'i' was dead after the end
of the 'for' loop (which I suppose it pretty common). Since 'i' was
now unsigned, the 'i>=0'' test is *always* true, and the lower loop
will never end.

As it happens, in every realistic scenario, the 'lots of code here'
part would end up returning, which is probably why the bug wasn't
caught.

DS
 
R

Richard Tobin

Rainer Weikusat said:
Please consider moving the discussion about 'personals and
personalities' into a talk show or some other region of the world
reserved for useless, antagonizing talk about 'others'.

I guess I shouldn't have fed the troll.

-- Richard
 
D

David Schwartz

It communicates that someone wrote (unsigned) for some reason and that,
taking the semantics of C into account, this was a reason of a
non-technical nature, ie a magic incantation intended placate some
Cerberus on the way to elysium, such as a code reviewer known to
enforce random, aesthetic preferences of his own.

It would be obvious to anyone who reviews C code professionally that
the cast was put there to indicate to someone looking at the code that
the conversion was intended and the person who put the cast in there
is vouching for its safety. It says, "I know the range of the type on
the right exceeds the range of the type on the left, and I have made
sure that the value cannot actually be out of range."

Now, if it doesn't mean that, then you are correct, it was put there
for some crazy, possibly erroneous, reason.
If 'programmer writing code which converts' is not sufficient to
assume 'programmer intended conversion', 'programmer writing
$anything' is technically not sufficient to assume that 'programmer
wanted to write $thing' or 'programmer should have written this
$thing' --- which is the actual problem: It doesn't matter the least
what the person who wrote the code intended to write. What matters is
what he wrote and if this works as it should work. In particular, in
case of any type of programming error, what the author intended to
write is not what should have been written.

There are multiple separate issues. One is whether the code does what
the author intended it to do. Another is whether the code does what
the code is supposed to do (based on a specification or common sense
or just 'it clearly shouldn't crash). Both are important.

If code clearly shows what the author intended, it is easier to
perform the review. First, you make sure what the programmer intended
does what needs to be done. Then you make sure that the code does
what's intended. You can't wholly separate them, but is often helpful
to be able to at least mostly do that.

This is especially true for code that's subject to elevated auditing
requirements such as encryption code or gambling device code or code
that's intended to serve as an example. The person who is the expert
on the algorithmics may not be the best person to catch C language
subtleties, and vice versa. (Though some of the techniques I'm talking
about may not be appropriate for use in example code precisely because
the person it is an example for may not be familiar with the
techniques. Obviously, every technique has both appropriate and
inappropriate uses.)

This is one of the reasons I generally prefer to hire programmers with
less experience. They have less unlearning to do. ;)

DS
 
M

Måns Rullgård

David Schwartz said:
Bluntly, I have been doing this for years and that has never happened
as far as I know. The reverse has happened many, many times, and
continues to happen on a regular basis.

Rainer's example is a pretty good description of the place I work.
If you have never had the misfortune to work in such a place, consider
yourself lucky.
 
R

Richard Bos

David Schwartz said:
Since that code is broken, it should be prohibited. Consider:

#define PRINTER_ON 0x100
#define PRINTER_HAS_PAPER 0x200
#define PRINTER_NO_ERROR 0x400
#define ready (PRINTER_ON|PRINTER_HAS_PAPER|PRINTER_NO_ERROR)

Oops, now the printer is ready if it's on even if it has no paper.
That can't be right.

The alternative would, I suppose, be this:

if ((printer_state & ready) == 0)

(or even, oh horrors, if (0 == (printer_state & ready))...)

Now consider:

#define PRINTER_CAN_PRINT 010
#define PRINTER_CAN_REROUTE 020
#define ready (PRINTER_CAN_PRINT|PRINTER_CAN_REROUTE)

Now it is not enough that the printer you chose can either print the job
itself, or reroute it to one of the other printers in that print group;
no, to be "ready", a printer must be able to do both. Pull the paper
drawer out of one single printer, and suddenly none of the printers in
the group are ready to print a job. That can't be right.

Of course, there are situations in which omitting the explicit test
against zero is wrong, and there are situations in which including that
test is wrong. But neither is quite as wrong as prohibiting either
idiom, full stop, in all situations.

Richard
 
K

Keith Thompson

David Schwartz said:
That's fine. Both of those things still communicate the essential
facts, which is that the programmer knew that the conversion would
occur and intended it.

To restore some context, the code in question was:

unsigned j;
int64_t q;

j=q;
vs.
j=(unsigned)q;

The says nothing else about the programmer's knowledge, at least not
directly. What it says *to me* is that the programmer probably
doesn't the language well enough, since he apparently isn't aware that
the cast does nothing.

Now if you have a coding convention that says that casts are to be
used only to indicate that a conversion (that would otherwise be done
implicitly) is known to be safe, and if you're able to assume that the
programmer follows that convention unfailingly, then yes, you can
assume that the conversion is safe -- or at least that the programmer
thought it was.
Why would it warn about the cast? No compiler warns about that cast --
it does exactly what the programmer obviously expected. The problem
with "j=q;" is that there is no way to know that the programmer
*intended* the conversion.

The programmer wrote an assignment that requires a conversion. If he
didn't intend the conversion, then he's not paying attention -- and
adding a cast doesn't prove anything different.

You may well be correct that no compiler would warn about the cast,
but one certainly could. In particular, it could warn about the
possible loss of information caused by the conversion, if the value of
q isn't within the range 0 .. UINT_MAX. Given that the assignment
with the cast means exactly the same thing as the assignment without
the cast, why should the compiler warn about one but not the other?

But as you say, the cast is likely to inhibit the warning. How do you
know the programmer had actually done the analysis to prove that the
conversion is safe, rather than just doing the equivalent of taping
over a warning light on his car's dashboard? We've seen plenty of
cases here in comp.lang.c where programmers do exactly that, adding a
cast just to shut up a warning (for example, casting the result of
malloc() rather than adding the required "#include <stdio.h>").

If you work in an environment where a cast really does indicate that
the programmer knows what he's doing, that's unusual. More commonly,
it indicates that the programmer *thinks* he knows what he's doing.

[...]
Comments should only be used where it's either not possible or
significantly inferior to make the code self-commenting. In this case,
the cast makes the code self-commenting.

I disagree, as I've explained. The cast could mean any of a number of
things, and unless you wrote the code yourself in the last, say, 6
months, you have no real basis for assuming any particular meaning.

[...]
I presume you meant disadvantage.

Yes, thanks for the correction.

[...]
 
D

David Schwartz

Now if you have a coding convention that says that casts are to be
used only to indicate that a conversion (that would otherwise be done
implicitly) is known to be safe, and if you're able to assume that the
programmer follows that convention unfailingly, then yes, you can
assume that the conversion is safe -- or at least that the programmer
thought it was.

On what planet do you need knowledge of some coding convention to know
that if a programmer explicitly requested a conversion by using a
cast, he mast have thought it was safe?!

The coding convention is only needed to know that this is the only
reason the cast is there and that it's unlikely that the programmer
mistakenly thought the cast was necessary. But even if you make this
mistake, it is harmless. You may think less of the programmer, but the
code will be correctly understood. The conversion is there, the
programmer knew it.

On the flip side, if the programmer omits the cast, there is a chance
of at least two serious misunderstandings. First, the programmer may
not have realized there was a conversion and not have made sure it was
safe. The reviewer might assume the programmer did know that. Second,
even if the programmer did realize there was a conversion and did
believe it was safe, the reviewer might not realize there's a
conversion, and might not review whether the programmer's conclusion
that it was safe was correct.

So you are pointing out a risk that the programmer will be thought
less of by an idiotic reviewer who doesn't realize the programmer is
avoiding two possible serious mistakes by putting the cast in.
The programmer wrote an assignment that requires a conversion.  If he
didn't intend the conversion, then he's not paying attention -- and
adding a cast doesn't prove anything different.

Adding the cast proves that he did intend the conversion. And it
alerts a reviewer that there is a conversion.
You may well be correct that no compiler would warn about the cast,
but one certainly could.  In particular, it could warn about the
possible loss of information caused by the conversion, if the value of
q isn't within the range 0 .. UINT_MAX.  Given that the assignment
with the cast means exactly the same thing as the assignment without
the cast, why should the compiler warn about one but not the other?

Because in one case there's a possibility that programmers and
reviewers won't check that the conversion is safe. In the other, there
is virtually no such possibility.
But as you say, the cast is likely to inhibit the warning.  How do you
know the programmer had actually done the analysis to prove that the
conversion is safe, rather than just doing the equivalent of taping
over a warning light on his car's dashboard?

You don't. That's why the cast is so important, because it flags the
case for you, so you do know you need to check it. If the cast is not
there, you won't realize that the programmer's analysis that the
conversion was safe needs to be reviewed.
 We've seen plenty of
cases here in comp.lang.c where programmers do exactly that, adding a
cast just to shut up a warning (for example, casting the result of
malloc() rather than adding the required "#include <stdio.h>").

Right, and the cast sticks out like a sore thumb to a reviewer. While
If you work in an environment where a cast really does indicate that
the programmer knows what he's doing, that's unusual.  More commonly,
it indicates that the programmer *thinks* he knows what he's doing.

It always indicates that the programmer knows there is a conversion
and intended it.
I disagree, as I've explained.  The cast could mean any of a number of
things, and unless you wrote the code yourself in the last, say, 6
months, you have no real basis for assuming any particular meaning.

This is a claim about how people will actually understand the code. I
disagree with it. The misunderstanding you are claiming could happen
has never happened in my experience. In contrast, I have seen dozens
of cases where a 'hidden conversion' and its consequences were missed
and real bugs resulted.

There is simply no other way to understand it other than "the
programmer knows there is a conversion here and believes that
conversion is appropriate". He may or may not think the cast is
necessary, but that really doesn't matter, since that has no effect on
the code.

DS
 
R

Rainer Weikusat

I guess I shouldn't have fed the troll.

Since you are the one who keeps trying to poison a (so far) purely
technical debate by posting insults about some of the people who
happen to have stated their support for a particular opinion (or, more
correctly, only insulting me while I wasn't the only one with this
particular standpoint), you are the 'troll' here.
 
R

Rainer Weikusat

David Schwartz said:
It would be obvious to anyone who reviews C code professionally that
the cast was put there to indicate to someone looking at the code that
the conversion was intended and the person who put the cast in there
is vouching for its safety.

As someone else already wrote: What you describe above is technically
a comment containing meta-information about the code. And using a
comment, it is actually possible to document this fact, eg

a = b; /* truncate to unsigned */

while this remains a mere assumption for the meaningless cast.
It says, "I know the range of the type on the right exceeds the
range of the type on the left, and I have made sure that the value
cannot actually be out of range."

The definition of 'cast operator' is

Preceding an expression by a parenthesized type name converts
the value of the expression to the named type. This
construction is called a cast.
[6.5.4|4]

This implies that 'casts' are supposed to be used to request that
value conversion are performed, not to document that value conversions
will never happen, as you said above.
Now, if it doesn't mean that, then you are correct, it was put there
for some crazy, possibly erroneous, reason.

It is not 'erroneous' to do 'lossy conversion' of values in
C. That's a feature the language has always had (AFAIK) since it was
invented. The assumption that noone could possibly want to do this,
which is even more visible in the extended assumption that using an
assignment which causes such a value conversion to happen would be
prima facie evidence of 'the programmer' not knowing that the
conversion will happen, despite that this would be the exact opposite
of the defined meaning of it, is somewhat far fetched. For instance,
one way to serialize an integer in little-endian byte order,
indepedent of host byte order (assuming 4 byte [unsigned] ints) would
be:

uint8_t serialize_u32(uint32_t v, uint8_t *p)
{
*p++ = v;
*p++ = v >> 8;
*p++ = v >> 16;
*p++ = v >> 24;

return p;
}

It is perfectly clear from the code that its author didn't expect to
be able to store a complete uint32_t value in a single byte (uint8_t
object).

[...]
There are multiple separate issues. One is whether the code does what
the author intended it to do. Another is whether the code does what
the code is supposed to do (based on a specification or common sense
or just 'it clearly shouldn't crash). Both are important.

What the code author intended to do isn't important. For instance, it
is completely possible that the author intended to do something which
would have been wrong, but failed at actually accomplishing that, and
- by accident - wrote correct code. This can only be determined by
examining the code itself. And 'the code' has the nice property that
it is there and open to inspection, will reasoning about the
'intentions' of someone, beyond what he actually communicated, will
always necessarily involve (unverifiable) assumptions.

[...]
This is one of the reasons I generally prefer to hire programmers with
less experience. They have less unlearning to do. ;)

This means little more except that you prefer to hire people without
the necessary practice to actually perform certain tasks, because this
might mean they would attempt to perform in a way different from the
way you would like it to be performed, or, in other words, that you
are opposed to the idea that 'programming', like any other craft (not
art) can be learnt (and taught) independently of the people
involved. I would assume that this was true at some time in the past
for more traditional professions, too, eg that there was a time when
doctors were generally believed to be wizards predetermined for their
profession by some mysterious, superhuman power (or builders, tool
makers, carpenters, tailers and so on).

I hope to live long enough to still see the proto-scientific 'stone ages'
end in this particular case, too.
 
K

Keith Thompson

David Schwartz said:
Now if you have a coding convention that says that casts are to be
used only to indicate that a conversion (that would otherwise be done
implicitly) is known to be safe, and if you're able to assume that the
programmer follows that convention unfailingly, then yes, you can
assume that the conversion is safe -- or at least that the programmer
thought it was.

On what planet do you need knowledge of some coding convention to know
that if a programmer explicitly requested a conversion by using a
cast, he mast have thought it was safe?!
Earth.

[...]

So you are pointing out a risk that the programmer will be thought
less of by an idiotic reviewer who doesn't realize the programmer is
avoiding two possible serious mistakes by putting the cast in.

No, I'm pointing out that the code means exactly the same thing with
or without the cast, and that you cannot safely assume what the
programmer meant by adding a completely unnecessary construct.

[...]
There is simply no other way to understand it other than "the
programmer knows there is a conversion here and believes that
conversion is appropriate". He may or may not think the cast is
necessary, but that really doesn't matter, since that has no effect on
the code.

I suggest that you're assuming your own experience is universal. At
the very least, I've demonstrated that there certainly are other ways
to understand it.

I don't think either of us is going to convince the other.
 
C

CBFalconer

Martin said:
.... snip ...

Since <comp.os.linux.advocacy> has nothing to do with this thread,
I have removed it. Even though it is unlikely that the thread is
relevant to <comp.os.linux,development.apps>, I don't know that
for sure and have left it. I apologize to people in that
newsgroup if it appears that we are polluting it.

If you do such a thing (as you have) I suggest you use the
"Followup-to" header item. This at least allows people on the
removed groups to notice.
 
D

Default User

CBFalconer said:
If you do such a thing (as you have) I suggest you use the
"Followup-to" header item. This at least allows people on the
removed groups to notice.

How is that? That's less likely to have people notice, in my
experience. If a message doesn't belong in a newsgroup, then you
shouldn't post it there. Arranging to have replies to your message not
directed there doesn't really accomplish that.



Brian
 
C

CBFalconer

Default said:
How is that? That's less likely to have people notice, in my
experience. If a message doesn't belong in a newsgroup, then you
shouldn't post it there. Arranging to have replies to your message
not directed there doesn't really accomplish that.

He removed the advocacy group. Any reader there never saw his
reply. With the follow-up, that reader would see the reply. Apart
from that, assuming accurate readers, the effects are the same.
 
D

David Schwartz

As someone else already wrote: What you describe above is technically
a comment containing meta-information about the code. And using a
comment, it is actually possible to document this fact, eg

        a = b;  /* truncate to unsigned */

This has the disadvantages I mentioned, the biggest one being that
it's much harder for automated testing tools to understand.
while this remains a mere assumption for the meaningless cast.

That's like saying that if the code says "i++;" concluding that the
programmer wanted to add one to 'i' is a mere assumption.
The definition of 'cast operator' is

        Preceding an expression by a parenthesized type name converts
        the value of the expression to the named type. This
        construction is called a cast.
        [6.5.4|4]

This implies that 'casts' are supposed to be used to request that
value conversion are performed, not to document that value conversions
will never happen, as you said above.

I didn't say a conversion wouldn't happen, I said the value wouldn't
be out of range. (Or that if it was out of range, the programmer had
somehow ensured that didn't break anything.)
It is not 'erroneous' to do 'lossy conversion' of values in
C. That's a feature the language has always had (AFAIK) since it was
invented. The assumption that noone could possibly want to do this,
which is even more visible in the extended assumption that using an
assignment which causes such a value conversion to happen would be
prima facie evidence of 'the programmer' not knowing that the
conversion will happen, despite that this would be the exact opposite
of the defined meaning of it, is somewhat far fetched. For instance,
one way to serialize an integer in little-endian byte order,
indepedent of host byte order (assuming 4 byte [unsigned] ints) would
be:

I never said the conversion couldn't be lossy. I said it couldn't be
out of range, that is, undefined. For example, if a programmer clearly
intentionally decrements an unsigned, he is vouching for the value not
being zero or for him intending the consequences of decrementing zero
if it is zero.

When code *clearly* expresses a specific idea, we can assume the
programmer intended that idea. We don't rely on this assumption, of
course, but it's our starting point. The code "i=j;" does not clearly
express an intent to convert, but 'i=(unsigned)j;' does.
        uint8_t serialize_u32(uint32_t v, uint8_t *p)
        {
                *p++ = v;
                *p++ = v >> 8;
                *p++ = v >> 16;
                *p++ = v >> 24;

                return p;
        }

It is perfectly clear from the code that its author didn't expect to
be able to store a complete uint32_t value in a single byte (uint8_t
object).

I agree. Here, the types are so close together that it would be absurd
to assume the programmer didn't intend the conversions. However, an
automated testing tool couldn't tell. You may or may not care about
that.
What the code author intended to do isn't important. For instance, it
is completely possible that the author intended to do something which
would have been wrong, but failed at actually accomplishing that, and
- by accident - wrote correct code.

That's true. But this will be easier to diagnose generally by seeing
that this is what happened. It's unlikely that you will conclude that
the code was correct and intended. But if that happens, you're right,
it doesn't matter. But that rarely happens.
This can only be determined by
examining the code itself. And 'the code' has the nice property that
it is there and open to inspection, will reasoning about the
'intentions' of someone, beyond what he actually communicated, will
always necessarily involve (unverifiable) assumptions.

It's unavoidable. When reviewing the work of a human being, you have
to try to figure out what they intended. And automated testing tools
even more so.

DS
 
D

David Schwartz

No, I'm pointing out that the code means exactly the same thing with
or without the cast, and that you cannot safely assume what the
programmer meant by adding a completely unnecessary construct.

Thank you for responding to an argument I never made.

DS
 
J

Joe Pfeiffer

David Schwartz said:
That's like saying that if the code says "i++;" concluding that the
programmer wanted to add one to 'i' is a mere assumption.

Not quite -- i++ has actual meaning, while the cast is, as has been
pointed out, not really semantically meaningful.

While the people comparing the cast to a comment have a point, an
important distinction is that this comment is explaining the code to
both a reader and to the compiler. The comments people have been
suggesting in its place are only comments to the reader.
 
S

Sergei Organov

[...]
int i;
for(i=0; i<GetMaxValue(foo); i++)
{
/* code goes here */
}
/* lots of code here */
for(i=GetMaxValue(foo); i>=0; i--)
{
/* code goes here */
}

But then someone noticed that 'GetMaxValue' returned an unsigned and
decided to change the type of 'i' to 'unsigned' to match, not noticing
the lower loop. I guess they assumed that 'i' was dead after the end
of the 'for' loop (which I suppose it pretty common). Since 'i' was
now unsigned, the 'i>=0'' test is *always* true, and the lower loop
will never end.

Reasonably recent GCC should issue a warning "Comparison is always true
due to limited type range" or something like that on the second loop.

-- Sergei.
 

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,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top