"C vs java"

S

stremler

begin quoting Richard Heathfield said:
(e-mail address removed) said:


Well, for one thing, you can't cast structs or unions. (C99 muddies the
waters a little with compound literals, but the document was certainly
addressing C90, so I'll confine my answer to that.)

/me tests

Hm... indeed. You have to abuse void * instead of casting to get that
sort of thing to happen.
But my rejection of
"anything goes" was really a rebuttal of the very popular idea that
casting is some kind of magic wand that can turn anything into anything
else. It isn't. A cast converts a value from one type to another type, but
makes no guarantees that the conversion makes sense. For example, you can
cast a long int into a pointer to struct tm if you want, but that doesn't
mean you'll get anything useful out of it. In fact, almost all casting in
C is wrong.

I've always thought of a cast as an instruction to the compiler to trust
the programmer about types in this instance; a sort of "No, really, I know
what I'm doing, I want to treat this object as THAT type, truly, trust me."

If actual conversion of data (as opposed to coercion of type) is involved,
yah, it's probably wrong.
 
K

Keith Thompson

/me tests

Hm... indeed. You have to abuse void * instead of casting to get that
sort of thing to happen.

But it's not quite the same thing. A cast applied to a struct, if it
were legal, might plausibly convert between two sufficiently similar
struct types by copying member values.
I've always thought of a cast as an instruction to the compiler to trust
the programmer about types in this instance; a sort of "No, really, I know
what I'm doing, I want to treat this object as THAT type, truly, trust me."

If actual conversion of data (as opposed to coercion of type) is involved,
yah, it's probably wrong.

A cast performs a value conversion; it doesn't (necessarily) treat an
object as being of a specified type. For example, ``(double)42''
yields the value 42.0 of type double.

Pointer conversions *typically* work by reinterpreting the
representation as the specified type, but that's not how the operation
is defined; on an implementation where different pointers have
different representations, an actual conversion will have to be
performed. And converting a pointer to an integer *of a different
size* will have to trim or pad bits, and might have to do more than
that.

Pointer conversion can be used to implement type-punning of the
pointed-to object, but the conversion itself doesn't do type-punning.

The point is that most of the cases where a conversion is portable can
be expressed as an implicit conversion rather than a cast. Taking
advantage of an implicit conversion lets the compiler figure out the
relevant types, rather than requiring the programmer to do so (and to
get it right with no help from the compiler).
 
B

Ben Bacarisse

begin quoting Richard Heathfield <[email protected]> :

I've always thought of a cast as an instruction to the compiler to trust
the programmer about types in this instance; a sort of "No, really, I know
what I'm doing, I want to treat this object as THAT type, truly,
trust me."

It is these uses that are usually wrong, either because the conversion
is not guaranteed (as in Richard's example) or because the cast is
redundant (as in the classic cast on a malloc call).

The real meaning of a (correct) cast is most often: "please convert
the value to one of this type because that is correct in this
situation and you (the compiler) will get it wrong otherwise". For
example:

printf("%d bytes at %p\n", (int)sizeof *ip, (void *)ip);

or

void process(const char *const *strings);

int main(int argc, char **argv)
{
process((const char **)(argv + 1)); /* add outer const */
return 0;
}

In the first case, the default conversions will be wrong, and in the
second the types are not compatible without a little help.
If actual conversion of data (as opposed to coercion of type) is involved,
yah, it's probably wrong.

I don't think that conversion of data makes it any more or less likely
to be right. Maybe I've missed your point. What were you think of?
 
S

stremler

begin quoting Ben Bacarisse said:
(e-mail address removed) writes:

It is these uses that are usually wrong, either because the conversion
is not guaranteed (as in Richard's example) or because the cast is
redundant (as in the classic cast on a malloc call).

I'm not comfortable considering redundant "wrong". Next you'll be
telling me that excessive parenthesis is "wrong" as well. "Wrong"
means that the resulting code does something other than what the
programmer (or reader of the code) expects.
The real meaning of a (correct) cast is most often: "please convert
the value to one of this type because that is correct in this
situation and you (the compiler) will get it wrong otherwise". [snip]
If actual conversion of data (as opposed to coercion of type) is involved,
yah, it's probably wrong.

I don't think that conversion of data makes it any more or less likely
to be right. Maybe I've missed your point. What were you think of?

When I read "conversion of data", I think the bit-patterns have changed.
When I read "coercion of type", I think the bit-patterns remain exactly
the same, but that their interpretation changes.

Where am I going wrong?
 
B

Ben Bacarisse

When I read "conversion of data", I think the bit-patterns have changed.
When I read "coercion of type", I think the bit-patterns remain exactly
the same, but that their interpretation changes.

Where am I going wrong?

The two are linked and it can be misleading to equate value and "bit
pattern". I am not saying you are just warning that you can get lead
astray that way.

To take two examples, given 'double pi = 3.14;' (int)pi and pi are
different values, don't compare equal and, if the int is stored in an
int variable, the bit patters are different.

People often assume that pointer conversions are no-ops at the machine
level but on a word-addressed machine, the conversion of, say, an int
pointer to void * might generate code to shift the bits to the left.
The bit pattern is different and the two pointers can't be compared.
They will, of course, compare equal if one is again converted to the
type of the other but, the cast caused the data to change.

<aside>
I am not sure if the value has changed in this case. A week ago, I'd
have said no -- we have two different representations (with different
types) of the same value: a pointer to a particular int. That was
before I'd read the definition of "value" in the standard. For
reference it is:

"precise meaning of the contents of an object when interpreted as
having a specific type"

Given an int *ip, I don't think that is enough to say whether ip and
(void *)ip have the same value. Either the definition does not apply
(because (void *)ip is not an object) or, by allowing the natural
extension to expressions, we have to know what "specific type" is to
be used. The obvious one is the type of the object or expression and
by that interpretation no two expressions of different type can have
the same value. If that is what it means then a cast always changes
the value if it changes the type.

If someone has another interpretation that makes more sense I'll take
it!
</aside>
 
B

Barry Schwarz

I'm not comfortable considering redundant "wrong". Next you'll be
telling me that excessive parenthesis is "wrong" as well. "Wrong"
means that the resulting code does something other than what the
programmer (or reader of the code) expects.

There is one difference. Unnecessary parentheses don't change the
action of the compiler. A superfluous cast can cause the compiler to
suppress a diagnostic. Consider the case of the "objected to" cast of
the return value of malloc. The cast itself is not "wrong" but if you
forget to include stdlib.h the compiler will not see the constraint
violation.
The real meaning of a (correct) cast is most often: "please convert
the value to one of this type because that is correct in this
situation and you (the compiler) will get it wrong otherwise". [snip]
If actual conversion of data (as opposed to coercion of type) is involved,
yah, it's probably wrong.

I don't think that conversion of data makes it any more or less likely
to be right. Maybe I've missed your point. What were you think of?

When I read "conversion of data", I think the bit-patterns have changed.
When I read "coercion of type", I think the bit-patterns remain exactly
the same, but that their interpretation changes.

Where am I going wrong?


Remove del for email
 
R

Richard Bos

I'm not comfortable considering redundant "wrong". Next you'll be
telling me that excessive parenthesis is "wrong" as well.

The difference between those two is that excessive parentheses are like
a wire that's better insulated than necessary, while an excessive cast
is like a high voltage sign on a battery. The first will do no harm; the
second will _probably_ do no harm _of itself_, but at the very least it
will tempt the user into ignoring other, more valid high voltage signs/
casts, and at worst, they can hide a real wiring/conversion error.
"Wrong" means that the resulting code does something other than what
the programmer (or reader of the code) expects.

Many excessive casts _can_ mean that. Won't always, but can.

Richard
 
K

Keith Thompson

CBFalconer said:
pete said:
The evaluation of these two expressions:
(0u == 0)
(UINT_MAX == -1)
requires conversions of the int type sub expressions to type
unsigned. But those signed int values may or may not have the
same bit patterns as the unsigned int values to which they
compare equal.

To the contrary, that is guaranteed by the standard. Section 6.2.5:

[#9] The range of nonnegative values of a signed integer
type is a subrange of the corresponding unsigned integer
type, and the representation of the same value in each type
is the same.28) A computation involving unsigned operands
can never overflow, because a result that cannot be
represented by the resulting unsigned integer type is
reduced modulo the number that is one greater than the
largest value that can be represented by the resulting type.

That guarantees that 0u and 0 have the same representation. It
doesn't guarantee that UINT_MAX and -1 have the same representation --
and in fact they don't on non-two's-complement systems.
 
L

lawrence.jones

Barry Schwarz said:
There is one difference. Unnecessary parentheses don't change the
action of the compiler.

#include <stdio.h>

int main()
{
int i;

if (i = 1) printf("Warning\n");
if ((i = 1)) printf("No warning\n");
return 0;
}

bash-2.02$ gcc -c -Wall test.c
test.c: In function `main':
test.c:7: warning: suggest parentheses around assignment used as truth value

-- Larry Jones

They can make me do it, but they can't make me do it with dignity. -- Calvin
 
S

stremler

\begin quoting Barry Schwarz said:
begin quoting Ben Bacarisse said:
(e-mail address removed) writes:
begin quoting Richard Heathfield <[email protected]> : [snip]
It is these uses that are usually wrong, either because the conversion
is not guaranteed (as in Richard's example) or because the cast is
redundant (as in the classic cast on a malloc call).

I'm not comfortable considering redundant "wrong". Next you'll be
telling me that excessive parenthesis is "wrong" as well. "Wrong"
means that the resulting code does something other than what the
programmer (or reader of the code) expects.

There is one difference. Unnecessary parentheses don't change the
action of the compiler. A superfluous cast can cause the compiler to
suppress a diagnostic. Consider the case of the "objected to" cast of
the return value of malloc. The cast itself is not "wrong" but if you
forget to include stdlib.h the compiler will not see the constraint
violation.

Which may indeed be unwise.

Got that. :)
 
S

stremler

/b/e/g/i/n/ quoting Richard Bos said:
The difference between those two is that excessive parentheses are like
a wire that's better insulated than necessary, while an excessive cast
is like a high voltage sign on a battery. The first will do no harm; the
second will _probably_ do no harm _of itself_, but at the very least it
will tempt the user into ignoring other, more valid high voltage signs/
casts, and at worst, they can hide a real wiring/conversion error.

I'll buy that. Thanks.
Many excessive casts _can_ mean that. Won't always, but can.

Which brings us back to casts changing things or not.

From my (limited) experiments with "gcc -S", there's no difference in the
resulting output whether or not I cast (some things). Is there a case where
the output *would* be different?
 
B

Ben Bacarisse

Which brings us back to casts changing things or not.

From my (limited) experiments with "gcc -S", there's no difference in the
resulting output whether or not I cast (some things). Is there a case where
the output *would* be different?

I would have thought that was clear from previous answers. For
example:

#include <stdio.h>

int main(int argc, char **argv)
{
printf("%f\n", (double)argc);
return 0;
}

I also posted an example of pointer cast that must generate code. It
seems sufficiently clear that I suspect I've missed your point.
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top