Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pointer Casts

12 views
Skip to first unread message

Simon Foster

unread,
Jul 21, 1998, 3:00:00 AM7/21/98
to
H&S states that explicit pointer casts are generally a good idea, but
( according to the FAQ ) an explicit cast of the pointer returned from
"malloc" is a bad idea. Are explicit casts a good idea? does this
also apply to casts to/from the void * type? Should we differentiate
between casts to void * and casts of other pointer types?

I am asking this because someone at work ( my boss !) has decided that
all implicit casts to void * should be made explicit. Does anyone
have any comments? My opinion is that casts to/from void * should be
a matter of style...

Perhaps while we're at it we should start a thread called :

"Wooohhh, Andrew Koenig is reading, does anyone have a question
about his book(s)?"
--
Simon Foster
Cheltenham
England

Richard Stamp

unread,
Jul 21, 1998, 3:00:00 AM7/21/98
to
In article <35b524d2.104781532@news>,

Simon Foster <si...@uggs.demon.co.uk> wrote:
>H&S states that explicit pointer casts are generally a good idea,

Hm, yes they do say that in section 5.3.3 (in the fourth edition) but
look at the context. They're cautioning against the assumption that
all pointers have a uniform representation and hence that you can
just assign between them at will. By contrast in section 5.3.1 they
talk explicitly about void * and the examples they use don't have
casts (which is a sort of implicit blessing).

H&S tries to be an all-inclusive book, they don't just talk about
Standard C. So from time to time they do recommend things simply
because they work with old (or even broken!) implementations, and
sometimes they talk about things which the Standard doesn't guarantee.
Normally they're quite good at marking these things out, but you do
have to be a little cautious here and there.

>I am asking this because someone at work ( my boss !) has decided that
>all implicit casts to void * should be made explicit. Does anyone
>have any comments? My opinion is that casts to/from void * should be
>a matter of style...

This has been done to death, really, mostly in the context of malloc's
return value. 95% of posters will say that the explicit cast can
conceal errors and is bad. Personally I don't use it because it just
seems pointless.

>"Wooohhh, Andrew Koenig is reading, does anyone have a question
> about his book(s)?"

Kind of intimidating, isn't it? If either of "K" or "R" shows up
I'm leaving. :-)

Cheers,
Richard
--
Richard Stamp
Churchill College, Cambridge

Derek Harmon

unread,
Jul 21, 1998, 3:00:00 AM7/21/98
to
Simon Foster wrote:
> H&S states that explicit pointer casts are generally a good idea, but
> ( according to the FAQ ) an explicit cast of the pointer returned from
> "malloc" is a bad idea. Are explicit casts a good idea?

Explicit casts ARE a good idea. The FAQ explains that perform-
ing an explicit cast on malloc() in ANSI standard C (which alleviated
the need to do so) may cover-up some warnings you would get if you
#include'd the wrong malloc().

My personal opinion is that all explicit casts are a good idea
(except when you start casting default promotions, then you are getting
carried away.) An explicit cast says, "I am the programmer, and I know
what I am doing."

If you don't know what you're doing -- BOOM!

But in general, you know what you're doing.

As for the argument for not explicitly casting the void pointer
returned by the correct malloc() from stdlib, my advice is simple:

If you use malloc() then #INCLUDE <stdlib.h> !!!

> does this
> also apply to casts to/from the void * type? Should we differentiate
> between casts to void * and casts of other pointer types?

You should differentiate between casts from void * and casts
from void *malloc()... that said, if you follow Derek's Golden Rule
which I'll repeat:

If you use malloc() then #INCLUDE <stdlib.h> !!!

Then you don't have to differentiate your pointer casts, you
should just doomall (do them all.)

> I am asking this because someone at work ( my boss !) has decided that
> all implicit casts to void * should be made explicit. Does anyone
> have any comments?

The only argument is (i) about casting away some warning messages
if you didn't include <stdlib.h> in the specific case of malloc() and
(ii) not typing explicit casts is easier and less likely to cause carpal
tunnel syndrome.

For advocates of (ii) I recommend you declare an editor macro.
As far as (i) goes, you know where I stand.

In favor of doing the casts are the arguments for readability,
maintainability, and ease of debugging (unless you forgot to include
<stdlib.h>). You probably don't appreciate these because you're well
familiar with your company's software, but when you go to a new shop,
and you have to learn their software, those casts next to malloc()
will make the learning go a little bit faster (or an absence of any
casts anywhere will make it go slower.)

Hopefully, your next employer won't expect you to make bug
fixes in their legacy code by tommorrow. :D

Derek Harmon

Ben Pfaff

unread,
Jul 21, 1998, 3:00:00 AM7/21/98
to
Derek Harmon <de...@ix.netcom.com> writes:

Explicit casts ARE a good idea. The FAQ explains that perform-
ing an explicit cast on malloc() in ANSI standard C (which alleviated
the need to do so) may cover-up some warnings you would get if you
#include'd the wrong malloc().

You have a contradiction here. Why would you want to cover up
warnings from including the wrong malloc()? That's a reason to
*avoid* explicit casts.

How many different header files with malloc() in them do you have,
anyway? There should be exactly one per compiler implementation
AFAIK.
--
(supporter of the campaign for grumpiness where grumpiness is due in c.l.c)

Please: do not email me copies of your posts to comp.lang.c
do not ask me C questions via email; post them instead

John Kugelman

unread,
Jul 21, 1998, 3:00:00 AM7/21/98
to
Derek Harmon wrote:
>
> Simon Foster wrote:
> > H&S states that explicit pointer casts are generally a good idea, but
> > ( according to the FAQ ) an explicit cast of the pointer returned from
> > "malloc" is a bad idea. Are explicit casts a good idea?
>
> Explicit casts ARE a good idea. The FAQ explains that perform-
> ing an explicit cast on malloc() in ANSI standard C (which alleviated
> the need to do so) may cover-up some warnings you would get if you
> #include'd the wrong malloc().
>
> My personal opinion is that all explicit casts are a good idea
> (except when you start casting default promotions, then you are getting
> carried away.) An explicit cast says, "I am the programmer, and I know
> what I am doing."
>
> If you don't know what you're doing -- BOOM!
>
> But in general, you know what you're doing.

But why do you feel explicit casts are good? Do you like being able to
say, "Hey, compiler, shut up!"? If you find that you get warnings from
your compilers, inserting a cast is probably a *bad* idea. I've never
needed casts between pointer types in any of my code, even the
nonportable stuff.

> As for the argument for not explicitly casting the void pointer
> returned by the correct malloc() from stdlib, my advice is simple:
>
> If you use malloc() then #INCLUDE <stdlib.h> !!!

Yes, but what if you forget? If you have no cast, you will get a
warning. If you have one, you won't. What's your argument for having
an explicit cast?

Here's another argument against casts: What if the type of your pointer
changes? The type of your cast will now be incorrect, but your compiler
will probably say nothing because it thinks you know what you're doing.

> > does this
> > also apply to casts to/from the void * type? Should we differentiate
> > between casts to void * and casts of other pointer types?
>
> You should differentiate between casts from void * and casts
> from void *malloc()... that said, if you follow Derek's Golden Rule
> which I'll repeat:
>
> If you use malloc() then #INCLUDE <stdlib.h> !!!

No, I think the ANSI committee came up with that before you.

> Then you don't have to differentiate your pointer casts, you
> should just doomall (do them all.)

Why have any casts in the first place? The only argument for them you
listed is that they aid readability, but I personally find them
cumbersome and ugly. I don't like hacks.

--
John Kugelman. kuge...@mnsinc.com

I believe we can change anything.
I believe in my dream.
- Joe Satriani

Szu-Wen Huang

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
: Simon Foster wrote:
: > H&S states that explicit pointer casts are generally a good idea, but
: > ( according to the FAQ ) an explicit cast of the pointer returned from
: > "malloc" is a bad idea. Are explicit casts a good idea?

: Explicit casts ARE a good idea. The FAQ explains that perform-

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
: ing an explicit cast on malloc() in ANSI standard C (which alleviated


: the need to do so) may cover-up some warnings you would get if you
: #include'd the wrong malloc().

Your second sentence doesn't support your thesis statement.

: My personal opinion is that all explicit casts are a good idea
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
: (except when you start casting default promotions, then you are getting


: carried away.) An explicit cast says, "I am the programmer, and I know
: what I am doing."

So what is it, a fact (as presented in the first paragraph) or an
opinion?

: If you don't know what you're doing -- BOOM!

: But in general, you know what you're doing.

Actually, an explicit cast where one isn't needed tells me the
programmer isn't confident with C. Extraneous parentheses often
(not always) tell me the same thing.

Remember, *everything* you type into source code, including
comments or code with mere commentary value, need to be maintained.

: As for the argument for not explicitly casting the void pointer


: returned by the correct malloc() from stdlib, my advice is simple:

: If you use malloc() then #INCLUDE <stdlib.h> !!!

I presume you disable all warnings and errors on your compiler, then?
The machine is there to help the human reduce human errors, but not
when the human makes it hard for the machine.

: > does this


: > also apply to casts to/from the void * type? Should we differentiate
: > between casts to void * and casts of other pointer types?

: You should differentiate between casts from void * and casts
: from void *malloc()...

What does that mean?

[...]
: Then you don't have to differentiate your pointer casts, you


: should just doomall (do them all.)

Programming is a deliberate activity, and everything you type into
a C source should have a purpose. What is the purpose of using
unnecessary casts?

: > I am asking this because someone at work ( my boss !) has decided that


: > all implicit casts to void * should be made explicit. Does anyone
: > have any comments?

: The only argument is (i) about casting away some warning messages
: if you didn't include <stdlib.h> in the specific case of malloc() and
: (ii) not typing explicit casts is easier and less likely to cause carpal
: tunnel syndrome.

: For advocates of (ii) I recommend you declare an editor macro.
: As far as (i) goes, you know where I stand.

In other words, you advocate writing extraneous code that do nothing?
No, Derek, I think anybody who wants to add extraneous stuff to source
code should *justify* it, not by lack of ill-effect.

: In favor of doing the casts are the arguments for readability,


: maintainability, and ease of debugging (unless you forgot to include
: <stdlib.h>).

A bold assertion. How about some facts to support that?

: You probably don't appreciate these because you're well


: familiar with your company's software, but when you go to a new shop,
: and you have to learn their software, those casts next to malloc()
: will make the learning go a little bit faster (or an absence of any
: casts anywhere will make it go slower.)

[...]

I don't see how the presence or absence of casting affects the
learning curve. If anything, extraneous casting simply clutters
the code with non-functional tokens.

Lawrence Kirby

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <35b524d2.104781532@news>
si...@uggs.demon.co.uk "Simon Foster" writes:

>H&S states that explicit pointer casts are generally a good idea, but
>( according to the FAQ ) an explicit cast of the pointer returned from
>"malloc" is a bad idea.

Pointers conversions are a bad idea since they can very easily lead to
portability problems and nasty bugs. However if you must convert between
different pointer types then in most cases a cast is required by the
language. The exceptions are that the compiler can convert implicitly between
void * and other suitably qualified pointers to object or incomplete types
(i.e. enything except pointers to functions). A pointer to a type
can be implicitly converted to a pointer to a mor qualified derivative
of that type e.g. char * can be assigned to const char *.

> Are explicit casts a good idea?

No, because the sort of pointer conversion that require them are a bad idea.
Explicit casts for conversions that don't require them (e.g. to and
from void *) are also generally a bad idea (although C++ requires
conversions from void * to be cast).

> does this
>also apply to casts to/from the void * type? Should we differentiate
>between casts to void * and casts of other pointer types?

That is inevitable since the language requires explicit casts for the
latter case.

>I am asking this because someone at work ( my boss !) has decided that
>all implicit casts to void * should be made explicit. Does anyone

>have any comments? My opinion is that casts to/from void * should be
>a matter of style...

If it is just a matter of style then it is perfectly reasonable for your
boss to impose style guidelines, that is quite normal. The point in
question is whether there it more to it than just style. My view is that
code ends up cleaner and less error prone if not filled with redundant
casts.

--
-----------------------------------------
Lawrence Kirby | fr...@genesis.demon.co.uk
Wilts, England | 7073...@compuserve.com
-----------------------------------------


Lawrence Kirby

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <6p3a17$bsn$1...@pegasus.csx.cam.ac.uk>
rg...@cam.ac.uk "Richard Stamp" writes:

>In article <35b524d2.104781532@news>,
>Simon Foster <si...@uggs.demon.co.uk> wrote:

>>H&S states that explicit pointer casts are generally a good idea,
>

>Hm, yes they do say that in section 5.3.3 (in the fourth edition) but
>look at the context. They're cautioning against the assumption that
>all pointers have a uniform representation and hence that you can
>just assign between them at will.

You can't assign directly between say an int * and an unsigned * even if they
do have the same representation. You can assign between int * and void *
even if they don't have the same representation. Representation is simply
not relevant to whether casts are required or not.

Richard Stamp

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <901111...@genesis.demon.co.uk>,
Lawrence Kirby <fr...@genesis.demon.co.uk> wrote:

> rg...@cam.ac.uk "Richard Stamp" writes:
>>
>>Hm, yes they do say that in section 5.3.3 (in the fourth edition) but
>>look at the context. They're cautioning against the assumption that
>>all pointers have a uniform representation and hence that you can
>>just assign between them at will.
>
>You can't assign directly between say an int * and an unsigned * even if they
>do have the same representation. You can assign between int * and void *
>even if they don't have the same representation. Representation is simply
>not relevant to whether casts are required or not.

Yes, I understand that (and so, one would expect, do H&S). I probably
ought to have quoted the passage in question; better late than never:

5.3.3 Some cautions with pointers.

Many C programmers assume that all pointer types have a unique
representation. On common byte-addressed computers all pointers are
typically simple byte addresses occuplying, say, one word.
Conversions among pointers and integer types on these computers
require no change in representation and no information is lost.

In fact, the C language does not require such nice behaviour.

[...]

As I said, you have to remember that H&S doesn't deal exclusively with
Standard C and some of the things they talk about (and generally caution
against) are practices that were assumed to work in the past. In the
case of assigning between different pointer types without casts, it
seems that this was allowed with some older compilers (I didn't use C
then, but the Rationale appears to imply that this is true). So I think
the quoted passage, and the advice about casting pointers -- which, as
you note, are really two distinct issues -- is intended as an introduction
to the regime in Standard C, and hence things that we in this group would
consider hard and fast rules, rather than mere advice.

By contrast they don't appear to say anything against implicit conversion
to/from void *, and their discussion of malloc(), for example, stresses
that the cast isn't needed in ISO C. So I don't think the book is really
at odds with the FAQ, although it's perhaps true that they could have
expressed themselves more clearly.

Martin Ambuhl

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to

Derek Harmon wrote in message <35B54008...@ix.netcom.com>...

| In favor of doing the casts are the arguments for readability,
|maintainability, and ease of debugging (unless you forgot to include

|<stdlib.h>). You probably don't appreciate these because you're well


|familiar with your company's software, but when you go to a new shop,
|and you have to learn their software, those casts next to malloc()
|will make the learning go a little bit faster (or an absence of any
|casts anywhere will make it go slower.)


As someone who has had to do a lot of maintenance on other people's code
(OPC), I find nothing above persuasive. In fact, I would rewrite this
as

Against doing the casts are the arguments for readability,
maintainability, and ease of debugging (especially you forgot to include
<stdlib.h>). You probably don't appreciate these because you're well


familiar with your company's software, but when you go to a new shop,
and you have to learn their software, those casts next to malloc() will

make the learning go a little bit slower (or an absence of any casts
anywhere will make it go faster.)

You can see that we completely disagree as to which is more readable or
easier to maintain and debug. And there is not a word in either the
initial paragraph or my rewriting which can help decide this issue.


Martin Ambuhl

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to

Simon Foster wrote in message <35b524d2.104781532@news>...
|H&S states that explicit pointer casts are generally a good idea, but
=======
I'm not sure where you see this in H&S

section 4.7 teaches that implicit declarations are bad, using malloc as
an example of how they can break programs. The cast of the return from
malloc is exactly what lets this go undetected.

section 6.1.3 tells us that NOT casting the result from (a properly
prototyped) malloc is safe.

Nothing in 5.8, 5.11.6, 6.3.2, or 7.5.1 suggests that the explicit cast
is a good idea.

It is true that H&S have a number of caveats about porting to
"traditional" (non-ISO) implementations.
=========


|( according to the FAQ ) an explicit cast of the pointer returned from

|"malloc" is a bad idea. Are explicit casts a good idea? does this


|also apply to casts to/from the void * type? Should we differentiate
|between casts to void * and casts of other pointer types?
|

|I am asking this because someone at work ( my boss !) has decided that
|all implicit casts to void * should be made explicit. Does anyone
|have any comments? My opinion is that casts to/from void * should be
|a matter of style...

=========
Actually, I dislike the cast for another reason than the "failure to
#include <stdlib.h>" problem.

Suppose I have a variable of type T * named x.
I can write code of the form:
x = malloc(n * sizeof *x);
without regard for the type of x.
Suppose I must cast the return from malloc.
One of the following must hold:
1) I know the type of x. This involves either making the type
explicit of typedefing types for every such possible T.
2) Using a non-standard extension (such as GCC provides):
x = (typeof x) malloc(n * sizeof *x);
Choice 2 is clearly not acceptable for code which might end up
anywhere but in a GCC implementation.
Choice 1 is namespace polluting and destroys type-transparency.

I know that this is not that large a deal: functions need the
appropiate type information in the prototypes; functions should be small
enough to this is not a problem; globals should be avoided. So perhaps
this is only an aesthetic (matter of taste) issue.

Matters of style are often regulated. The question is what matter
of style should be policy and which should be individual taste. The
danger of omitting correct prototypes suggests that if this is a policy
question, then it should be decided in favor of NOT casting.

Martin Ambuhl (mam...@tiac.net)
/* Newsgroup posts also e-mailed */
==========


|
|Perhaps while we're at it we should start a thread called :
|

|"Wooohhh, Andrew Koenig is reading, does anyone have a question
| about his book(s)?"

Derek Harmon

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
Ben Pfaff wrote:
> You have a contradiction here. Why would you want to cover up
> warnings from including the wrong malloc()? That's a reason to
> *avoid* explicit casts.

It's only a contradiction in that I offered both my own viewpoint
(explicit casts are good) but also decided out of fairness, to cite that
the FAQ states casts on malloc() are bad.

It's not a reason to avoid explicit casts if instead of remembering
not to cast any void pointers, you instead remember to always include the
stdlib.h header when you use malloc().

> How many different header files with malloc() in them do you have,
> anyway? There should be exactly one per compiler implementation
> AFAIK.

Personally, I have two headers. ;) Really, the standard stdlib.h
one and the nonstandard alloc.h one (which nobody uses but came with the
compiler.) However, where is it forbidden for a company to create its
own internal malloc() for whatever reason and forsake the stdlib.h malloc
in favor for mycompany.h malloc?

Not that I'm suggesting that's a good idea: you also forsake all
of stdlib.h and end up confusing the hell out of your coders -- but things
like that have been known to happen.

Derek Harmon

Derek Harmon

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
John Kugelman wrote:

> Derek Harmon wrote:
> > But in general, you know what you're doing.
>
> But why do you feel explicit casts are good? Do you like being able to
> say, "Hey, compiler, shut up!"? If you find that you get warnings from
> your compilers, inserting a cast is probably a *bad* idea.

I hate warnings -- I have them all turned on, and I fight tooth and
nail to get them all out of my code. To cite a recent example, in a post
I made last night, "Re: 3D Arrays and Functions" for which I removed the
cast from malloc just to satisfy the tsunami of response I'd get for
"casting malloc." Without the cast, I get the warning "Suspicious Pointer
Conversion," with the cast it goes away. Good idea or Bad idea?

Program works. Following the guidelines for accessing the 3D
Array through the settor and accessor functions provided, it will work.
Now, if somebody wants to come along on a 3 x 3 x 3 array and try to
index 28 x 128 x 99.. then BOOM of course.. but keeping to its interface,
Good idea.

But warning or no warning, it works. Frankly, I'd just as soon
have the cast there to remember what data was (of course, an identifier
in Hungarian Notation also works, but it was only a short example).

> > If you use malloc() then #INCLUDE <stdlib.h> !!!
>

> Yes, but what if you forget? If you have no cast, you will get a
> warning. If you have one, you won't.

Forgetting #include <stdlib.h> should produce an unknown symbol
malloc (or somesuch) error, never mind the warnings! :) The problem
rears its ugly head with including another header that has a malformed
malloc! Just don't do it. Just say no. :)

Derek's Silver Rule.. "If you use a function, include it's
header!"


> > If you use malloc() then #INCLUDE <stdlib.h> !!!
>

> No, I think the ANSI committee came up with that before you.

Well, then they're smart folks! :D Or maybe it's just a good idea.

> Here's another argument against casts: What if the type of your pointer
> changes? The type of your cast will now be incorrect, but your compiler

> will probably say nothing because it thinks you know what you're doing.
:

> Why have any casts in the first place? The only argument for them you
> listed is that they aid readability, but I personally find them
> cumbersome and ugly. I don't like hacks.

Type of pointer changes? Do you mean going through the same
piece of code with the same pointer and one time its type A and the next
time its type B? Sounds like a hack to me.

No examples come to mind.. I'm keen if anyone has a pointer
changing, same pointer, with two or more possible types, going through
the same code where a cast that is right for one of those types will
break it for the others. (And then I'm willing to hear how it
demonstrates good programming practice.. ;D )

No warning can protect you from that, because the compiler can't
know the types your pointers may have during execution, whether you cast
them or not. But if you don't know what to cast them to, then that's a
warning sign right there.

This situation is most particular to void pointers, and the usage
of void pointers is an entirely different matter. But there are reasons
for the limitations of what can be done to void pointers (such as pointer
arithmetic).

I'll acknowledge the following three uses for void pointers,
where void pointers are, for _brief_ periods, able to produce a more
expediant or versatile solution:

(1) malloc, calloc, functions returning a generic pointer.
In these cases, the void pointer ceases to be useful as a
void pointer once it's been delivered to the caller, and
should be cast.

(2) free. In this case the void pointer is no longer valid
after the call (I mean void pointer here in an internal to
free sense). You can cast it going in (void *) but that's
not neccessary.

(3) memcpy, functions operating on unidentifiable pointers
within a bufferspace. These void pointers are also internal
to the functions. You can cast it going in (void *) but
thats unneccessary.

If there is a void pointer outside of these contexts, then
ask yourself why? Perhaps the application fits into one of these
classifications, or is using the void pointers for their greatest
utility: passing a generic location. The important thing is that
when that pointer gets to where its going, the authors and maintain-
ers of that code should _know_ what type it is.

If most of the time in some code a coder doesn't know what
type a void pointer is, then that code is dangerous! First, its very
unreadable in a static sense. That is, the code's behavior isn't
apparent to an inspection of the source code, only to an analysis of
its dynamic execution state(s). This makes it harder to learn and
debug. In these cases, the solution ISN'T to remove casts and get
warnings (its most dangerous when you can't get warnings, because a
compiler is limited in its ability to predict your program's behavior
once its syntactically satisfied). It's to cut back on void pointers,
and cast where possible.

A properly designed solution shouldn't need to make heavy
use of void pointers outside of the above contexts, and in cases
where one receives a void pointer, one should know enough at that
point in the program to make a cast.


Derek Harmon
===
A Man on a Mission... to rid the world of cast-less programming,
the use of varargs functions, the GOTO keyword and other crutchs.

Simon Foster

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
rg...@cam.ac.uk (Richard Stamp) wrote:

>In article <35b524d2.104781532@news>,
>Simon Foster <si...@uggs.demon.co.uk> wrote:

>>H&S states that explicit pointer casts are generally a good idea,
>

>Hm, yes they do say that in section 5.3.3 (in the fourth edition) but
>look at the context. They're cautioning against the assumption that
>all pointers have a uniform representation and hence that you can

>just assign between them at will. By contrast in section 5.3.1 they
>talk explicitly about void * and the examples they use don't have
>casts (which is a sort of implicit blessing).
>

However, I also note that in the section where they talk about the
return value from malloc(), they are specifically _not_ talking about
implicit void* casts in any other context. I'm not disagreeing with
you, I'm just wondering whether you could take section 5.3.1 as a
blessing when it is restricted to a very specific context.

>H&S tries to be an all-inclusive book, they don't just talk about
>Standard C. So from time to time they do recommend things simply
>because they work with old (or even broken!) implementations, and
>sometimes they talk about things which the Standard doesn't guarantee.
>Normally they're quite good at marking these things out, but you do
>have to be a little cautious here and there.
>

I agree here, I think H&S is a little too concerned about backwards
compatibility. Maybe the 5th edition will correct some of this. I
also think that H&S is _the_ best reference for the language bar none.


>>I am asking this because someone at work ( my boss !) has decided that
>>all implicit casts to void * should be made explicit. Does anyone
>>have any comments? My opinion is that casts to/from void * should be
>>a matter of style...
>

>This has been done to death, really, mostly in the context of malloc's
>return value. 95% of posters will say that the explicit cast can
>conceal errors and is bad. Personally I don't use it because it just
>seems pointless.
>


I'm really talking about casts to void* in a more general context. It
seems that the general thrust of your post is that they are
unnecessary - which is my feeling also.

>>"Wooohhh, Andrew Koenig is reading, does anyone have a question
>> about his book(s)?"
>

>Kind of intimidating, isn't it? If either of "K" or "R" shows up
>I'm leaving. :-)
>

>Cheers,
>Richard
>--
>Richard Stamp
>Churchill College, Cambridge

Ritchie has certainly posted on comp.lang.c before! And quite
recently. Don't worry, they're watching.

Simon Foster

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
fr...@genesis.demon.co.uk (Lawrence Kirby) wrote:

>In article <35b524d2.104781532@news>
> si...@uggs.demon.co.uk "Simon Foster" writes:
>

>>H&S states that explicit pointer casts are generally a good idea, but


>>( according to the FAQ ) an explicit cast of the pointer returned from
>>"malloc" is a bad idea.
>

>Pointers conversions are a bad idea since they can very easily lead to
>portability problems and nasty bugs. However if you must convert between
>different pointer types then in most cases a cast is required by the
>language. The exceptions are that the compiler can convert implicitly between
>void * and other suitably qualified pointers to object or incomplete types
>(i.e. enything except pointers to functions). A pointer to a type
>can be implicitly converted to a pointer to a mor qualified derivative
>of that type e.g. char * can be assigned to const char *.
>

>> Are explicit casts a good idea?
>

>No, because the sort of pointer conversion that require them are a bad idea.
>Explicit casts for conversions that don't require them (e.g. to and
>from void *) are also generally a bad idea (although C++ requires
>conversions from void * to be cast).
>

>> does this
>>also apply to casts to/from the void * type? Should we differentiate
>>between casts to void * and casts of other pointer types?
>

>That is inevitable since the language requires explicit casts for the
>latter case.
>

>>I am asking this because someone at work ( my boss !) has decided that
>>all implicit casts to void * should be made explicit. Does anyone
>>have any comments? My opinion is that casts to/from void * should be
>>a matter of style...
>

>If it is just a matter of style then it is perfectly reasonable for your
>boss to impose style guidelines, that is quite normal. The point in
>question is whether there it more to it than just style. My view is that
>code ends up cleaner and less error prone if not filled with redundant
>casts.
>

Agreed. My boss can decide whatever he likes. I leave on Friday. The
code fragment I am talking about now looks like this :

void* a = (void *)&array;

Which makes little sense. Take the address of the array, yielding a
pointer of type 'pointer to array of T', then cast to void*. I did
try to explain about the equivalence of arrays and pointers but it was
lost.


>--
>-----------------------------------------
>Lawrence Kirby | fr...@genesis.demon.co.uk
>Wilts, England | 7073...@compuserve.com
>-----------------------------------------
>

It's about now that I start wishing that I'd brought the Standard home
with me. I was reading it today. Doesn't the standard actually say
that casts should be made explicit? Maybe I'll have to look it up
tomorrow so that I can quote chapter and verse.

Simon Foster

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to

>
>>>"Wooohhh, Andrew Koenig is reading, does anyone have a question
>>> about his book(s)?"
>>
>>Kind of intimidating, isn't it? If either of "K" or "R" shows up
>>I'm leaving. :-)
>>
>>Cheers,
>>Richard
>>--
>>Richard Stamp
>>Churchill College, Cambridge
>
>Ritchie has certainly posted on comp.lang.c before! And quite
>recently. Don't worry, they're watching.
>
>
>--
>Simon Foster
>Cheltenham
>England


I knew it was here somewhere.... I believe this is genuine.


From: Dennis Ritchie <d...@bell-labs.com>
Subject: Re: Help w/ undefined behavior ( a = a++; )
Date: 27 Mar 1998 00:00:00 GMT
Message-ID: <351B3B...@bell-labs.com>
Content-Transfer-Encoding: 7bit
References: <6f9sai$54n$1...@client2.news.psi.net>
<Pine.LNX.3.95.98032...@metroid.dyn.ml.org>
<3518D4...@rug.ac.be> <6famma$37v$1...@lyra.csx.cam.ac.uk>
Mime-Version: 1.0
Reply-To: d...@bell-labs.com
Content-Type: text/plain; charset=us-ascii
Organization: Bell Labs, Lucent Technologies
Newsgroups: comp.lang.c,comp.std.c


Among other contributors, Nick Maclaren wrote on the perennial
subject of the meaning of sequence points:

> ... There is a serious ambiguity
> in the assumption of chronology, which I must check has been fixed
> in C9X. It occurs in constructions like:

> (a,++a,a)+(a,++a,a)

> But the original example of 'a = f(a++);' is not a problem.

I think it is worth admitting that sequence points (places
at which side effects become evident) were a useful addition
to the semantic description of the language in C89, but
also that neither it nor (so deeply as I have read) C9X
is fully helpful.

The problem is that the current and proposed standard
seem, at least in places, to assume that there is a total,
temporal ordering (during execution) of sequence points that
can be deduced from the textual ordering of the source
program. It would have been better to use the term
"partial order" explicitly and say what must come before
what and what's not ordered by the standard.

In particular saying that the , operator induces a sequence
point with respect to its operands (and not necessarily
with respect to a larger expression in which it occurs) might
help. Likewise--to fiddle the function call example--in

extern i;
a[i] = f(i++);

the fact that the argument sequence point implies
that within f's evaluation the increment must be seen
in the extern version of i does not necessarily mean that
the argument sequence point requires any ordering between
the evaluation of the address of a[i] and the i++ of the
argument.

Fully working this out to give concise descriptions of
what's OK and what's not is hard. The "maximum liberty"
principle would seem to call for a position in which
evaluation of

f() + g();

admits instruction-by-instruction interleaving of the
two functions, and damn the side-effects, a more radical
posture than anyone wants, even though all sequence
points in f and g are putatively unordered w.r.t. the sum
expression.

Fully functional-type languages in theory don't suffer from
this kind of problem, although in practice of course they
do (just not as obviously). Languages in which evaluation
order is fully specified don't either.

Dennis

Richard Stamp

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <35b6638e.12884210@news>,

Simon Foster <si...@uggs.demon.co.uk> wrote:
>
>However, I also note that in the section where they talk about the
>return value from malloc(), they are specifically _not_ talking about
>implicit void* casts in any other context. I'm not disagreeing with
>you, I'm just wondering whether you could take section 5.3.1 as a
>blessing when it is restricted to a very specific context.

I'm not sure that we're looking at the same section here -- 5.3.1 in my
copy (which is the fourth edition, 1995) talks about void * in a quite
general context. The examples are things like

void *generic_ptr;
int *int_ptr;

generic_ptr = int_ptr;

It's perhaps stretching it to talk about "blessing", but if they thought
you should use casts you'd expect them to insert them here. 6.3.2 also
talks about conversions to/from void * being allowed without any advice
to avoid this feature. I think overall they're quite neutral on the
issue, if you buy my argument that 5.3.3 is talking about something a
bit different.

>I'm really talking about casts to void* in a more general context. It
>seems that the general thrust of your post is that they are
>unnecessary - which is my feeling also.

Yeah, it's just my opinion but I do think they're unnecessary and
unhelpful (in modern C). Unless I've missed a particular case, the only
time you can get away without a cast is when you're immediately assigning
the void * to a pointer variable of the "right" type, so the argument
just reduces to whether you need additional cues to help you pick out the
type of a variable. If you don't, the cast is redundant because you know
the type anyway. If you do feel the additional cues are useful, then you
probably want to go the whole hog and use something like Hungarian
notation (after all, why address this one single instance of the problem
and leave the general problem unsolved)?

>Ritchie has certainly posted on comp.lang.c before! And quite
>recently. Don't worry, they're watching.

He's popped up in threads crossposted between here and comp.std.c, but
I don't think he's posted recently to this group alone. Of course, he
could still be reading. [In which case, hi, Dennis! Can I have your
autograph? :-)]

James Hu

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
On Wed, 22 Jul 1998 18:27:21 -0400, Derek Harmon <de...@ix.netcom.com> wrote:

> Forgetting #include <stdlib.h> should produce an unknown symbol
>malloc (or somesuch) error, never mind the warnings! :) The problem
>rears its ugly head with including another header that has a malformed
>malloc! Just don't do it. Just say no. :)

Sounds like you are compiling C with a C++ compiler. In C, it is not
an error to use a function that does not have a prototype in scope.
It defaults to a function accepting a fixed number of parameters which
returns an int. This is why casting is a bad idea for malloc, since
the cast can mask away the fact that an integer value is being
assigned to a pointer type variable.

For example, the following C program is correct:

/* beginning of file */
int main (void)
{
puts ("hello, world!");
return 0;
}
/* end of file */

I am not sure I can categorically condemn the use of casts on
pointers, however. They are often used to write the comparison
function for qsort() and bsearch(), for instance (although, they are
avoidable at the expense of declaring some extra variables).

In general, if one is dealing with void pointers, type safety is being
violated anyway. Therefore, with or without a cast is mostly a matter
of style.

--
James C. Hu <j...@cs.wustl.edu> Computer Science Doctoral Candidate
http://www.cs.wustl.edu/~jxh/ Washington University in Saint Louis
>>>>>>>>>>>>> I use *SpamBeGone* <URL:http://www.internz.com/SpamBeGone/>

Aaron Crane

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <35B66749...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> I hate warnings -- I have them all turned on, and I fight tooth and nail
> to get them all out of my code.

This is quite clearly a sensible approach.

> To cite a recent example, in a post I made last night, "Re: 3D Arrays and
> Functions" for which I removed the cast from malloc just to satisfy the
> tsunami of response I'd get for "casting malloc." Without the cast, I get
> the warning "Suspicious Pointer Conversion," with the cast it goes away.

If you've included <stdlib.h>, and your compiler is translating C and not
C++, then you should either turn off that specific warning, or, if you
can't, ditch the compiler and get a real one. That just isn't an
appropriate warning for C.

> Forgetting #include <stdlib.h> should produce an unknown symbol malloc (or
> somesuch) error, never mind the warnings! :)

No it shouldn't. The standard explicitly allows calling a function without
a declaration (let alone a prototype) in scope. Of course, a good compiler
may offer a warning for this case; that's a different matter entirely.

> Type of pointer changes? Do you mean going through the same piece of code
> with the same pointer and one time its type A and the next time its type
> B? Sounds like a hack to me.
>

> No examples come to mind. I'm keen if anyone has a pointer changing, same


> pointer, with two or more possible types, going through the same code
> where a cast that is right for one of those types will break it for the
> others. (And then I'm willing to hear how it demonstrates good
> programming practice.. ;D )

Suppose I have a program which dynamically allocates and array of floats to
do some signal processing work. One day, I decide that float isn't
sufficiently precise. So, I change the declaration

float *data;

to

double *data;

My malloc calls all looked like this:

data = malloc (n * sizeof data[0]);

and they all continue to work with no further changes. If my malloc calls
had been like this instead:

data = (float *) malloc (n * sizeof (float));

or even (not quite as bad):

data = (float *) malloc (n * sizeof data[0])

then I have to fix each case individually. (I have done this. It is
gruesome.)

> But if you don't know what to cast them to, then that's a warning sign
> right there.

The point is not that I don't know what to cast them to, but that I might
make reasonable changes at some time in the future which will cause the code
to be incorrect. This is called `optimising for maintainability' by some
people; it's almost invariably a good approach.

--
Aaron Crane <aaron...@pobox.com> <URL:http://pobox.com/~aaronc/>

Derek Harmon

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
Szu-Wen Huang wrote:

> Derek Harmon (de...@ix.netcom.com) wrote:
> : Explicit casts ARE a good idea. The FAQ explains that perform-
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> Your second sentence doesn't support your thesis statement.

I will say that perhaps a better transition between sentences
could've been made...

I wasn't making a thesis statement. I'm giving the original
poster both sides of the story. I'm not interested in winning a debate
about typecasting, though I prefer to use casts on void pointers myself.
I am explaining all information relevant to the original poster's
question.

> So what is it, a fact (as presented in the first paragraph) or an
> opinion?

Of course it's an opinion! You didn't read the entire post,
did you?

> : If you don't know what you're doing -- BOOM!
>
> : But in general, you know what you're doing.
>
> Actually, an explicit cast where one isn't needed tells me the
> programmer isn't confident with C. Extraneous parentheses often
> (not always) tell me the same thing.

I am totally dumbfounded where you read this in my post...
the original post, and my reply, dealt with making explicit typecasts
on void pointers. I think it's a good idea.

Confidence in C has nothing to do with it. If Dennis Ritchie
himself walked up to a 100,000 line C program with void pointers
everywhere and no casts, and you pointed to a void pointer and asked
him what type it was supposed to be -- he couldn't tell you.

If there is a cast next to the function returning that void
pointer -- then you know! He knows! The next ten consultants to be
charged with maintaining that code know!

> Remember, *everything* you type into source code, including
> comments or code with mere commentary value, need to be maintained.

Thank you. Exactly why it should be there. Using this argument,
and your statement about "Confidence in C," one might suspect you would
argue for "NO COMMENTS!"

> : As for the argument for not explicitly casting the void pointer
> : returned by the correct malloc() from stdlib, my advice is simple:
>
> : If you use malloc() then #INCLUDE <stdlib.h> !!!
>
> I presume you disable all warnings and errors on your compiler, then?
> The machine is there to help the human reduce human errors, but not
> when the human makes it hard for the machine.

The exact opposite, as my other posts in the thread explain.
How does including stdlib.h when you use malloc() make it hard on the
machine?

I mean, a lot of people talk about this error. The error isn't
in the typecast, the error was forgetting to include stdlib.h.

> : You should differentiate between casts from void * and casts
> : from void *malloc()...
>
> What does that mean?

Read the FAQ.

> Programming is a deliberate activity, and everything you type into
> a C source should have a purpose. What is the purpose of using
> unnecessary casts?

Unnecessary POINTER casts (see subject line).. of which there
aren't many: you don't have to cast a typed pointer to (void *), that
would be ridiculous, and you don't have to key in a cast when your
assigning between pointers of the same type.

All other explicit pointer casts are very useful. That doesn't
mean you apply them like a drone.. I won't code,

char *s1;
char *s2;
:
s1 = (char *)s2;

That is admittedly inane. However,

void *p1;
char *s1;
:
s1 = (char *)p1;

Is totally appropriate. If I make that assignment, I as programmer
had better know that p1 is actually pointing to something string-like. I
want to indicate this to myself six months from now, and my grandchildren
who'll be maintaining this code decades from now, that I have thoughtfully
affixed my stamp of approval to this assignment.

Not having a cast there won't make the compiler go into convulsions
if p1 happens to not point to something string-like because the compiler has
nothing to say about the dynamic traits of my code. However it is a plain-
ly evident requirement to anyone reviewing the code that p1 has an implied
precondition that it must point to something string-like, and that that
section of code must not be executed when p1 doesn't meet this precondition.
Any such execution would plainly be an error.

That's all said there, in that one line, with that one typecast
when seen by any competent C programmer. How many comments would you use?

> : In favor of doing the casts are the arguments for readability,


> : maintainability, and ease of debugging (unless you forgot to include
> : <stdlib.h>).
>

> A bold assertion. How about some facts to support that?

Seen many 100,000+ line C programs recently?

> I don't see how the presence or absence of casting affects the
> learning curve. If anything, extraneous casting simply clutters
> the code with non-functional tokens.

data = result;

Does THAT tell you anything?

Okay, how about,

data = (struct matrix_type *)result;

This line tells you plenty more information about what
limitations apply to result in this segment of code, and it tells you
outright what data's type is. That information could be 5, 10, 50
pages somewhere else in the hardcopy, in a different module.

What about this,

pResultMat = processLeontiefMat( &retrievedMat, 10);

Not bad, but how much clearer is this to someone just hired
to learn this code,

pResultMat = processLeontiefMat( (struct markhov_type *)&retrievedMat, 10);

Isn't it a joy not to go get a new binder that gives the prototype
of processLeontiefMat allows a struct markhov_type pointer there? You also
know retrievedMat must at least be a struct markhov_type, or a type more
complex but compatible with struct markhov_type. You might have to look six
or seven other places to locate all of this information up.

Debugging is made easier. You can trace backwards and categorically
affirm or refute that retrievedMat meets the criteria evident in that state-
ment. Learning is made easier, all the information is in one location and
not several. Maintainability is easier when late at night you feel a little
hazy about the different matrix structures.

I've seen and fixed a lot of code... I could go on. I've seen a
lot of things go wrong in code that would've been self-evident had the
original programmer inserted a typecast here or there to remind themselves
what they were doing.

Derek Harmon

Derek Harmon

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
Simon Foster wrote:
> I agree here, I think H&S is a little too concerned about backwards
> compatibility. Maybe the 5th edition will correct some of this. I
> also think that H&S is _the_ best reference for the language bar none.

Backwards compatibility _is_ important. Look at how much COBOL
there still is in the world. :) Besides, you never know what might be
done to pointer representations in the future. I'm no prognosticator,
but, what if they become balkanized? Then where's all your uncast code?

Don't think pointers will change? Then you must be all for
backward compatibility! :) It's the number one reason for them not
to change in the future. So let's not be too hasty in neglecting the
old code here.

> I'm really talking about casts to void* in a more general context. It
> seems that the general thrust of your post is that they are
> unnecessary - which is my feeling also.

I disagree with letting void* be in a general context (outside
specific circumstances where void* is useful) ... however I do fully
agree with the highlight you put on the real issue at hand here ...
void* in general. Not just casting malloc() and not superfluous
casts on integers (how that got injected into this thread, I'll never
know). :)
Derek Harmon

Derek Harmon

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
Richard Stamp wrote:
> generic_ptr = int_ptr;

Well here it is less necessary. When you use a generic pointer, its
seldom IT'S type we care about, under no circumstances should we use it as a
type. It's when you deposit that generic pointer into a typed pointer, then
you want the "cue" and you had better be darned certain its of the right type.

My personal style isn't to let void pointers carry a useful address
for long, when I do give them one. And when I remove an address from a void
pointer, it's only one type (or a compatible type) and I better know what
those are at that point in the code.

Generic pointers shouldn't be used everywhere, they aren't necessary
to solving the bulk of problems. They're like goto's. I can only think
I've heard the feedback I have because _everybody_ uses them _everywhere_
(and when I've seen that, the programs were usually _everbuggy_ or I would
suppose fragile to change).

> just reduces to whether you need additional cues to help you pick out the
> type of a variable. If you don't, the cast is redundant because you know
> the type anyway. If you do feel the additional cues are useful, then you

You know the type of the variable _now_. Wait til later.. months
separating you since you conceived of it, and totally new functionality
added elsewhere in the program that may just swoop down and nuke this
function while you weren't paying attention.

> probably want to go the whole hog and use something like Hungarian
> notation (after all, why address this one single instance of the problem
> and leave the general problem unsolved)?

I don't personally favor Hungarian Notation but I see a lot of
it (being a Windows type). The most important facet of the problem does
concern dealing with void pointers. "This is a pointer, I don't know what
type it is," isn't acceptable from a programmer. If a person sets a pointer
from a void pointer, they had better be certain what that void pointer was.
If you are certain, only good can come from putting the cast there to docu-
ment it.

Derek Harmon

Aaron Crane

unread,
Jul 22, 1998, 3:00:00 AM7/22/98
to
In article <35B693C9...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> data = result;
>
> Does THAT tell you anything?

If your result is a void *, there's likely to be some serious design
breakage in your program. How about this:

void *hash_lookup (const char *s); /* in a header file */

data = hash_lookup (s);

Doesn't that tell you everything you need to know? Is saying

data = (struct matrix_type *) hash_lookup (s);

*really* any clearer?

> Okay, how about,
>
> data = (struct matrix_type *)result;
>
> This line tells you plenty more information about what limitations apply
> to result in this segment of code, and it tells you outright what data's
> type is.

But I can see that information where I declared it, not more than, say, 30
lines away. If the function were any longer, it would be harder to
understand it.

> That information could be 5, 10, 50 pages somewhere else in the hardcopy,
> in a different module.

Then that's broken code anyway. If you're declaring something that far from
its use, then you deserve everything you get if you simply call it `data'.

> What about this,
>
> pResultMat = processLeontiefMat( &retrievedMat, 10);
>
> Not bad, but how much clearer is this to someone just hired
> to learn this code,
>
> pResultMat = processLeontiefMat( (struct markhov_type *)&retrievedMat, 10);

Both of these are (to my eyes) utterly illegible. Once again, if you can't
remember a variable's type for the whole of its scope, then your function is
*too long*. (And if you need a local variable name more than two or three
characters long, there are too many variables in your function.)

> Isn't it a joy not to go get a new binder that gives the prototype of
> processLeontiefMat allows a struct markhov_type pointer there? You also
> know retrievedMat must at least be a struct markhov_type, or a type more
> complex but compatible with struct markhov_type.

The only things compatible with the type (struct markhov_type *) apart from
(struct markhov_type *) itself are a (void *) and a pointer to a struct with
the same members. But inserting the cast will prevent the compiler from
checking that the types being assigned are in fact compatible. Casting
means `I am lying through my teeth about the type of my data' -- it is not
mere `documentation'. And the more you have to lie about your data, the
less maintainable your code is.

> Debugging is made easier. You can trace backwards and categorically
> affirm or refute that retrievedMat meets the criteria evident in that
> state- ment.

No you can't. All you can categorically determine is that the programmer
*said* that retrievedMat met the criteria. That says nothing very relevant.

Lawrence Kirby

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
In article <35b6658f.13397319@news> si...@uggs.demon.co.uk "Simon Foster" writes:

...

>Agreed. My boss can decide whatever he likes. I leave on Friday. The
>code fragment I am talking about now looks like this :
>
> void* a = (void *)&array;
>
>Which makes little sense. Take the address of the array, yielding a
>pointer of type 'pointer to array of T', then cast to void*. I did
>try to explain about the equivalence of arrays and pointers but it was
>lost.

There are two separate issues there: using &array instead of just array
and casting explicitly to void *. In this context there's probably not going
to be much difference between array and &array so which it makes more sense
to use depends on what a will be used for. Either way the cast is unnecessary
since without it the conversion to void * will happen implicitly in the
assignment.

...

>It's about now that I start wishing that I'd brought the Standard home
>with me. I was reading it today. Doesn't the standard actually say
>that casts should be made explicit?

Casts are by definition explicit. A cast is a syntactic construct in the
source code; it is an operator that has the form of a type enclosed in
parentheses and is described in subclause 6.3.4 of the standard. Not all
type conversions in C require casts. Notably here conversions between void *
and other pointer to object or incomplete types don't (when the context makes
the target type clear). See 6.2.2.3 and 6.3.16.1 (for implicit conversions
allowed by assignment. This is referred to by other parts of the standard
e.g. initialisations and argument conversions when calling a prototyped
function).

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Aaron Crane wrote:
> If you've included <stdlib.h>, and your compiler is translating C and not
> C++, then you should either turn off that specific warning, or, if you

Oh, another point on casting of void pointers, I know everyone will
shutter when I mention it: What if you must port to C++ someday?

IT managers have been known to do that lightly. :D

> > No examples come to mind. I'm keen if anyone has a pointer changing, same


> > pointer, with two or more possible types, going through the same code
> > where a cast that is right for one of those types will break it for the
> > others. (And then I'm willing to hear how it demonstrates good
> > programming practice.. ;D )
>

> Suppose I have a program which dynamically allocates and array of floats to
> do some signal processing work. One day, I decide that float isn't
> sufficiently precise. So, I change the declaration
>
> float *data;
> to
> double *data;
>
> My malloc calls all looked like this:
>
> data = malloc (n * sizeof data[0]);
>
> and they all continue to work with no further changes. If my malloc calls
> had been like this instead:
>
> data = (float *) malloc (n * sizeof (float));

Not the really devious hack I had read into the last poster's reply
(I tried to do it as I read it, but C always required me to cast the void
so it couldn't be two types) but a fair counterexample.

These sorts of code-wide changes can be quickly rendered with a
short Perl script. Call it discouragement from coding before completing
analysis, or encouragement to define a literal in lieu of the cast (always
good for anything that "may change tomorrow"),

#define DATA_CAST (float *)
#define DATA_TYPE (float)
:
data = (DATA_CAST) malloc(n * sizeof DATA_TYPE);

(I can hear it now.. "You used an extra parenthesis!".. chill.)

> The point is not that I don't know what to cast them to, but that I might
> make reasonable changes at some time in the future which will cause the code
> to be incorrect. This is called `optimising for maintainability' by some
> people; it's almost invariably a good approach.

I've seen cases of C programmers making reasonable changes somewhere
in the code that caused a bug to crop up elsewhere, by sending a previously
closed function a void pointing to a type that function shouldn't have been
passed. Whereas the presence of a typecast on an assignment from a void
pointer to another type should have raised an eyebrow, no typecast on that
assignment (and sheer numbers of untyped void pointer assignments) led to a
grueling amount of debugging. This didn't happen once to the programmers,
it happened dozens of times to one group. Of course, better comments on
what to pass and what not to pass were lacking and would've also helped.

I agree that optimizing for maintainability is a good approach,
we just have disparate views on maintainability. :)

Derek Harmon

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Aaron Crane wrote:
> If your result is a void *, there's likely to be some serious design
> breakage in your program. How about this:

Exactly why it should be cast, at least I had better be certain
that result is carrying a pointer to struct matrix_type. Everyone else
who looks at this code should be certain, too.

data = (struct matrix_type *)result;

:
> void *hash_lookup (const char *s); /* in a header file */

(Header file being slightly far away, first the interested party
has to identify which included header hash_lookup belongs to, then they
have to load that header, then if they want the details mentioned below,
they have to open the source for the header..)

>
> data = hash_lookup (s);
>
> Doesn't that tell you everything you need to know? Is saying

No.. (slight chuckle as I realize we're just on totally different
wavelengths, but that it's okay).

> data = (struct matrix_type *) hash_lookup (s);
>
> *really* any clearer?

Yes. It says hash_lookup returns a struct matrix_type * for
keys that s must be one of (or possibly NULL, if s is not a key
of a struct matrix_type and [important] not a key of any other type
returnable from hash_lookup()).

That is, if there are any problems with data at this point,
the thing I would check immediately is that s is a key for which
hash_lookup() should return a struct matrix_type *. If hash_lookup()
returns a struct complex *, then I know either hash_lookup() has
logic problems, or s at this point in the program is somehow screwed
up. So I start backtracking what's happened to s.

Note the only reason to make hash_lookup() void * is either
to evade granting hash_lookup() visibility of struct matrix_type, or
because hash_lookup() can return pointers to other structs than just
struct matrix_type. If it only returned one type, then having it
return a correctly typed pointer would be preferable (and a cast
wouldn't be necessary, unless it were a downcast).

The only other place to find this sort of information about
what a void * function returns is in it's implementation. You can
hardly condone going there to obtain this knowledge, can you?

If you didn't have this information at the spot, and in a
real-world example a dozen things could happen to data in this
function, then you'd have more work to do (as I said, rooting
through hash_lookup() to figure out what it returns and when..)
With the cast there, you know exactly what to look for.



> > That information could be 5, 10, 50 pages somewhere else in the hardcopy,
> > in a different module.
>

> Then that's broken code anyway. If you're declaring something that far from
> its use, then you deserve everything you get if you simply call it `data'.

Good point, bad identifier name used in that example. But it really
isn't difficult for a variable to travel that far, in code that takes up
seven binders on it's own bookshelf. The variable comes to you as a function
argument, and you don't know it's history. You hope that if it's had any
hanky panky with receiving void pointers before, it's been cast and those
regular checkpoints let you partition tracts of code in which to examine
it for problems.

Without regular checkpoints, there's no telling where something can
go wrong. Some variables just never die, Aaron, they just keep circulating
around the system. Then they get corrupted, and management wants to know
"Where?" and they want to know yesterday. :)

> Both of these are (to my eyes) utterly illegible. Once again, if you can't
> remember a variable's type for the whole of its scope, then your function is
> *too long*. (And if you need a local variable name more than two or three
> characters long, there are too many variables in your function.)

Whoa, first "data" is a bad variable name, now having variable names
longer than 3 characters is too long? :D I normally do use longer variable
names than data, obviously, identifiers that better represent what they are
supposed to be.

However, big pieces of software have thousands of variables (and
it's a fair bet that the largest single common name is 'i') ;D and they
can have lengthy scopes, be distributed and persistant. All I am saying
is when they do, you don't want to trust chance with a slew of uncast
void pointers. You want to know when you look at it, what it is, what
its preconditions were, etc. When assigning from a void * (let me be
clear, a practice I don't like, but when it's done..) you should cast
it, because you should know the type.

> The only things compatible with the type (struct markhov_type *) apart from
> (struct markhov_type *) itself are a (void *) and a pointer to a struct with
> the same members. But inserting the cast will prevent the compiler from
> checking that the types being assigned are in fact compatible. Casting

The compiler can't check that the types being assigned are in fact
compatible, the compiler only exists at compile time. If the example
void *lookup_hash(char *s) you gave can return 10 different types of
pointers, dependent on s, then only the caller has any rational ability
to know what to expect back.

> means `I am lying through my teeth about the type of my data' -- it is not
> mere `documentation'. And the more you have to lie about your data, the
> less maintainable your code is.
>

> > Debugging is made easier. You can trace backwards and categorically

: :


> No you can't. All you can categorically determine is that the programmer
> *said* that retrievedMat met the criteria. That says nothing very relevant.

There's not usually a reason for the programmer to lie to himself,
or other people associated with maintaining his code. If he resorts to
using a suspicious pointer cast, he should document it explicitly with a
cast to the type he expects.

If a problem arises, either he failed to ensure the requirements
of that typecast were lived up to, or somebody else using the function did.
The wrong sort of typecast on a returned pointer can manifest itself with
either totally bonkers data corruption, or a very prompt GPF. In either
case, a very direct trace can be made back to the typecast (hopefully
these typecasts are rare, because they should be rare, but they should
be made).

Derek Harmon

Chris Torek

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
This is a bit of a tangent, although it actually gets back to
the subject, at least.

In article <35B6ABC5...@ix.netcom.com> Derek Harmon


<de...@ix.netcom.com> writes:
>Besides, you never know what might be done to pointer representations
>in the future. I'm no prognosticator, but, what if they become
>balkanized? Then where's all your uncast code?

Casts or no casts, the code is pretty much in the same place.

Casts are conversions. Just as (double)3 changes "3" into "3.0"
(which usually has an entirely different bit pattern), (int *)p
changes whatever value p has -- presumably some pointer value that
is not already of type (int *) -- into some other value.

Now, suppose you have:

void *p;
int *ip;

and you set p to something valid -- perhaps a non-NULL return value
from a malloc() call -- and then write:

ip = p;

This assignment has the *same meaning* as:

ip = (int *)p;

because "void *" has a number of special cases woven into the C
Standard. On the other hand, if you write:

char *cp;

and then:

ip = cp; /* WRONG */

the Standard requires a diagnostic (after which the compiler can
do almost anything it wants to). To eliminate the diagnostic, you
must write:

ip = (int *)cp;

Note that the only difference between "ip = p;" and "ip = cp;" is
that the latter requires the cast to eliminate the diagnostic --
the C Standard requires that (char *) and (void *) use the same
internal representation. (This, unsurprisingly, is for backwards
compatibility with pre-ANSI compilers, where malloc() returned
"char *".)

Now, if you *do* write either of:

ip = p;
or ip = (int *)p;

the most important thing is that "p" *must* point to memory suitable
for holding an "int" object. If you pluck a value for p out of
thin air, there is a good chance it is *not* pointing to such
memory. The Standard says that the effect of such an assignment
is undefined.

On some machines, if the bit pattern in "p" is odd, or is not
congruent to zero mod 3, and you set ip=p, any attempt to use *ip
will cause a trap (a "bus error" or "segmentation fault" or "illegal
address" or some such). On other machines, the low bits of the
mystery value you stored into ip are ignored, or control something
else. THE PRESENCE OR ABSENCE OF A CAST HAS NO EFFECT on this, or
at least, *need* not have any effect, and usually does not. (Since
the overall effect is undefined, a compiler can, of course, do
anything it wants in this case -- including make the presence or
absence of a cast change the effect.)

Simiarly, suppose you have an ordinary "char" variable lying around:

char mych;

You may then set p or cp to point to this "char":

p = &mych;
cp = &mych;

but you cannot set ip to point to it:

ip = &mych; /* WRONG */

You *can* generally force this past a compiler by inserting a cast,
but again, the effect is undefined. This remains true even if you
use the "automatic" cast provided by "void *":

p = &mych;
ip = p; /* WRONG */

Moreover, if you just convert the new value (if any) in "ip" back
to (char *), the effect remains undefined:

p = &mych; /* ok */
ip = p; /* WRONG */
p = ip; /* ok */
cp = p; /* ok */

/* Even if we get here, */
if (cp == &mych) {
/* we may or may not ever get *here*. */
...
}

One reason for this is that on some machines -- typically word-oriented
machines like the Data General Eclipse -- a "word" pointer is made
from a "byte" pointer by shifting and/or masking and discarding
the "byte number" part of the pointer. A "byte" pointer is made
from a "word" pointer by again shifting or masking, and inserting
zero into the "byte number" part of the pointer. Thus, if &mych
happens to reside in "byte 3 of word 1295", the value in "cp" is
likely to point to "byte 0 of word 1295" -- some *other* "char"
than "mych".

Again, adding or removing casts, and/or using "void *" to "secretly"
insert and delete casts, makes no difference -- the fundamental
error of trying to shove a gallon of bits (the address of a char)
into a quart pail (an int pointer) remains.

You *can* -- because the Standard specifically allows it -- convert
any valid pointer into a "char *", and then convert it back to its
original type, and the results "shall compare equal". (That is,
they may not *be* equal, because the implementation might change
some otherwise unused bits along the way, but they must *look*
equal "from the outside", via C's == and != operators.) The same
holds for "void *", where casts are not required.

Whether to cast the return value from malloc() remains a style
issue, and, as Mark Brader either said or quoted someone else
saying, "while there are plenty of wrong styles, there is no single
right style."
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA Domain: to...@bsdi.com +1 510 234 3167
Antispam notice: unsolicited commercial email will be handled at my
consulting rate; pyramid-scheme mail will be forwarded to the FTC.

Szu-Wen Huang

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Aaron Crane (aaron...@pobox.com) wrote:
[...]
: Suppose I have a program which dynamically allocates and array of floats to

: do some signal processing work. One day, I decide that float isn't
: sufficiently precise. So, I change the declaration

: float *data;

: to

: double *data;

: My malloc calls all looked like this:

: data = malloc (n * sizeof data[0]);

: and they all continue to work with no further changes. If my malloc calls
: had been like this instead:

: data = (float *) malloc (n * sizeof (float));

: or even (not quite as bad):

: data = (float *) malloc (n * sizeof data[0])

: then I have to fix each case individually. (I have done this. It is
: gruesome.)

[...]

Indeed, which is why you should've used an abstract type when you
started. I think I may have a better example. Consider a program
that needs to send various IPC messages, each looking like:

void SendMessage1(...)
{
Message1_t *p;

p = malloc(sizeof *p);
if (p != NULL)
{
p->data1 = ...;
p->data2 = ...;
...
sysSend(p); /* will also free the buffer for us */
}
}

This is quite a candidate to cut and paste to create all the other
messages, but if you cast malloc, instead of one change you now must
make two per function. Multiply that by thirty messages, and it's
sufficiently annoying to make one ask "why cast?"

Szu-Wen Huang

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
[...]
: Confidence in C has nothing to do with it. If Dennis Ritchie

: himself walked up to a 100,000 line C program with void pointers
: everywhere and no casts, and you pointed to a void pointer and asked
: him what type it was supposed to be -- he couldn't tell you.

: If there is a cast next to the function returning that void
: pointer -- then you know! He knows! The next ten consultants to be
: charged with maintaining that code know!

I disagree. A program where the type of a particular variable isn't
immediately obvious is already a poor program to start with. Global
variables should be easy to find, and local variables should be even
easier to find.

: > Remember, *everything* you type into source code, including


: > comments or code with mere commentary value, need to be maintained.

: Thank you. Exactly why it should be there. Using this argument,
: and your statement about "Confidence in C," one might suspect you would
: argue for "NO COMMENTS!"

No, I argue for placing variable declarations where they can be found,
instead of littering type comments all over the place. Are you in
favor of:

int a, b, c, d;

/* int */ a = /* int */ b + /* int */ c * /* int */ d;

? The natural extension of your unnecessary casts would indeed be
as above.

[...]
: > I presume you disable all warnings and errors on your compiler, then?


: > The machine is there to help the human reduce human errors, but not
: > when the human makes it hard for the machine.

: The exact opposite, as my other posts in the thread explain.
: How does including stdlib.h when you use malloc() make it hard on the
: machine?

The unnecessary cast masks an error.

: I mean, a lot of people talk about this error. The error isn't


: in the typecast, the error was forgetting to include stdlib.h.

The typecast masks the error, for no gain. This isn't a big deal,
because many if not most compilers will warn against the identifier
anyway.

: > : You should differentiate between casts from void * and casts
: > : from void *malloc()...

: > What does that mean?

: Read the FAQ.

If I were interested in Steve Summit's opinion, I'd indeed read the
FAQ. I was asking what *you* meant.

: > Programming is a deliberate activity, and everything you type into


: > a C source should have a purpose. What is the purpose of using
: > unnecessary casts?

: Unnecessary POINTER casts (see subject line).. of which there
: aren't many: you don't have to cast a typed pointer to (void *), that
: would be ridiculous, and you don't have to key in a cast when your
: assigning between pointers of the same type.

: All other explicit pointer casts are very useful. That doesn't
: mean you apply them like a drone.. I won't code,

: char *s1;
: char *s2;
: :
: s1 = (char *)s2;

: That is admittedly inane. However,

: void *p1;
: char *s1;
: :
: s1 = (char *)p1;

: Is totally appropriate. If I make that assignment, I as programmer
: had better know that p1 is actually pointing to something string-like. I
: want to indicate this to myself six months from now, and my grandchildren
: who'll be maintaining this code decades from now, that I have thoughtfully
: affixed my stamp of approval to this assignment.

It's dangerous to rely on programming habits that are programmer-
dependent. If I always use only spaces to indent, then indeed a
line with tabs is suspect. However, how would you comment if I relied
on such a subtle device for correctness?

In other words, how would I, as a maintenance programmer who doesn't
know you personally, decide whether you were "just commenting" or
simply didn't know what you were doing? If you want to put a comment,
I believe a comment, not unnecessary code, is the best device.

: Not having a cast there won't make the compiler go into convulsions


: if p1 happens to not point to something string-like because the compiler has
: nothing to say about the dynamic traits of my code. However it is a plain-
: ly evident requirement to anyone reviewing the code that p1 has an implied
: precondition that it must point to something string-like, and that that
: section of code must not be executed when p1 doesn't meet this precondition.
: Any such execution would plainly be an error.

: That's all said there, in that one line, with that one typecast
: when seen by any competent C programmer. How many comments would you use?

/* p1 should hold a string */

: > : In favor of doing the casts are the arguments for readability,


: > : maintainability, and ease of debugging (unless you forgot to include
: > : <stdlib.h>).
: >
: > A bold assertion. How about some facts to support that?

: Seen many 100,000+ line C programs recently?

Yes, actually, and I find myself wondering if the programmer knew his/
her stuff when I see a cast *operator* doing what a comment should.

: > I don't see how the presence or absence of casting affects the


: > learning curve. If anything, extraneous casting simply clutters
: > the code with non-functional tokens.

: data = result;

: Does THAT tell you anything?

No, it doesn't, because of the choice of stupid identifiers names,
and the absolute impossibility of finding out what type each are.
I *do* know that they're compatible types, though.

: Okay, how about,

: data = (struct matrix_type *)result;

: This line tells you plenty more information about what
: limitations apply to result in this segment of code, and it tells you
: outright what data's type is. That information could be 5, 10, 50
: pages somewhere else in the hardcopy, in a different module.

That makes it a stupid program to start with. The inability to place
variable declarations where they are easily found is not something
one fixes with "cast comments".

Also, tools like c2html, which can take you to the declaration at
the click of a button, makes your entire point moot.

: What about this,

: pResultMat = processLeontiefMat( &retrievedMat, 10);

: Not bad, but how much clearer is this to someone just hired
: to learn this code,

: pResultMat = processLeontiefMat( (struct markhov_type *)&retrievedMat, 10);

: Isn't it a joy not to go get a new binder that gives the prototype
: of processLeontiefMat allows a struct markhov_type pointer there? You also

: know retrievedMat must at least be a struct markhov_type, or a type more
: complex but compatible with struct markhov_type. You might have to look six
: or seven other places to locate all of this information up.

It may also be a genuine bug, covered up by the extraneous cast by
an idiot programmer. The point isn't whether a good programmer can
do something safely, but whether you can tell or not.

: Debugging is made easier. You can trace backwards and categorically


: affirm or refute that retrievedMat meets the criteria evident in that state-
: ment. Learning is made easier, all the information is in one location and
: not several. Maintainability is easier when late at night you feel a little
: hazy about the different matrix structures.

By the way, have you considered hungarian notation? It does exactly
what you're talking about, without the danger of hiding bugs from
your compiler.

: I've seen and fixed a lot of code... I could go on. I've seen a


: lot of things go wrong in code that would've been self-evident had the
: original programmer inserted a typecast here or there to remind themselves
: what they were doing.

I certainly wouldn't go into a pissing contest about who's seen more
code, but from those that I've seen, I wouldn't assume the original
programmer to be competent and trust an extraneous cast.

Let's put it this way. Would you assume, in a program you didn't
write, that a cast is a comment rather than a bug?

Szu-Wen Huang

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
: Aaron Crane wrote:
[...]
: > data = (struct matrix_type *) hash_lookup (s);
: >
: > *really* any clearer?

: Yes. It says hash_lookup returns a struct matrix_type * for
: keys that s must be one of (or possibly NULL, if s is not a key
: of a struct matrix_type and [important] not a key of any other type
: returnable from hash_lookup()).

[...]

No, it says that whatever idiot changes the return type of hash_lookup,
pretend it's a struct matrix_type *, and nevermind the bug. The
uncast version will result in a compile-time error.

[...]
: Without regular checkpoints, there's no telling where something can


: go wrong. Some variables just never die, Aaron, they just keep circulating
: around the system. Then they get corrupted, and management wants to know
: "Where?" and they want to know yesterday. :)

So stop passing them around, and make them die within a manageable life.
Do you realize you've been defending your practice with a whole load of
bad practices? If your cast is useful, it's because:

- idiot (I don't mean you) named the variable
- idiot declared the variable far away in non-obvious place
- idiot can't maintain variable's lifetime

If you're so worried about idiots, why ignore the possibility that
they'll change a type and not go over all 100,000 lines to see where
it's used? Your helpful casts, at that point, will quietly compile!

[...]
: There's not usually a reason for the programmer to lie to himself,


: or other people associated with maintaining his code. If he resorts to
: using a suspicious pointer cast, he should document it explicitly with a
: cast to the type he expects.

It appears to me that a comment will do everything you want without
all the other risks we cite.

: If a problem arises, either he failed to ensure the requirements


: of that typecast were lived up to, or somebody else using the function did.
: The wrong sort of typecast on a returned pointer can manifest itself with
: either totally bonkers data corruption, or a very prompt GPF. In either
: case, a very direct trace can be made back to the typecast (hopefully
: these typecasts are rare, because they should be rare, but they should
: be made).

As opposed to a compile-time error that any real compiler can direct
you to the very line of?

Steve Summit

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
In article <35B54008...@ix.netcom.com>,
Derek Harmon <de...@ix.netcom.com> evidently wrote:
> Explicit casts ARE a good idea. The FAQ explains that performing an

> explicit cast on malloc() in ANSI standard C (which alleviated the
> need to do so) may cover-up some warnings you would get if you
> #include'd the wrong malloc().

What the FAQ list is trying to say is that the explicit casts
might hide warnings resulting from malloc not being declared
(i.e. no header declaring it being included) at all. This is
of course supposed to be an argument in favor of the hypothesis
that explicit casts are a bad idea.

Steve Summit
s...@eskimo.com

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Chris Torek wrote:
> This is a bit of a tangent, although it actually gets back to
> the subject, at least.
[agreeable points snipped]

:
> ip = cp; /* WRONG */
>
> the Standard requires a diagnostic (after which the compiler can
> do almost anything it wants to). To eliminate the diagnostic, you
> must write:
>
> ip = (int *)cp;

That is, the compiler gives you a warning for the first case.
The second case demonstrates (as was said in another post) the programmer
saying, "Compiler Shutup!" :) If you don't like to cast shaky pointer
assignments, then you either (i) don't do shaky pointer assignments (which
I applaude, best choice, but not as I understand it the original post's
question dealing with void*s), (ii) do them and ignore the warnings,
(iii) do them and have the compiler's warnings turned off.

That's why you need a cast there, _if_ you know what you're doing.
I'm not saying it's a good idea to do that [the assignment], it all depends
on the situation. But if you do things like that, there should be an
explicit cast there. If you don't know enough about the state of the
program at that point to put an explicit cast there, then you really need
to re-assess why you're making that assignment.

One of the original respondents poised a really hard to conceive
of example of a pointer coming through the code with two different types..
I have not been able to write such code to demonstrate it without the
compiler requiring a cast of me.

:


> Whether to cast the return value from malloc() remains a style
> issue, and, as Mark Brader either said or quoted someone else
> saying, "while there are plenty of wrong styles, there is no single
> right style."

Agreed. Although I can't understand why one would choose not to
cast the return value from malloc() when the only benefit is to warn
that you didn't include the right headers. I guess I just take including
<stdlib.h> as something you always do before you even call malloc().

Derek Harmon

Chris Engebretson

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
In article <35B77133...@ix.netcom.com>,
Derek Harmon <de...@ix.netcom.com> writes:

|> Chris Torek wrote:
|>
|> > This is a bit of a tangent, although it actually gets back to
|> > the subject, at least.
|> [agreeable points snipped]
|> :
|> > ip = cp; /* WRONG */
|> >
|> > the Standard requires a diagnostic (after which the compiler can
|> > do almost anything it wants to). To eliminate the diagnostic, you
|> > must write:
|> >
|> > ip = (int *)cp;
|>
|> That is, the compiler gives you a warning for the first case.

While a C implementation can warn about anything that it likes,
the first example that Chris gives goes a bit beyond a warning.
This type of pointer assignment, outside of the context of an
explicit cast, is a constraint violation; with that in mind, the
implementation can simply stop translating altogether.

|> The second case demonstrates (as was said in another post)
|> the programmer saying, "Compiler Shutup!" :) If you don't

|> like to cast shaky pointer assignments, then you either ..

[ snip ]

If you don't like to cast "shaky" pointer assignments (by which
I assume we're talking about conversions between pointers to
two different object types), then you're not writing legal C.

|> That's why you need a cast there, _if_ you know what you're
|> doing.

You need a cast there because the language requires it. Your
particular implementation may only issue a warning and continue
the translation, but this is not required behavior. When your
implementation issues complaints of this manner, it is generally
extremely unwise to ignore it.

|> Agreed. Although I can't understand why one would choose not
|> to cast the return value from malloc() when the only benefit is
|> to warn that you didn't include the right headers.

That is most assuredly not the only benefit.

Other than force of habit, I can't understand why one would choose
*to* cast the return value of *alloc(). It's not a particularly
difficult habit to break. Some years back I was faced with a
series of nightmarish problems when porting a legacy system to a
platform where sizeof(int) was smaller than sizeof(char *). The
"cast habit" was an easy monkey to get off my back after going
through and fixing the innumerable cast-induced bugs.

The decision of whether or not to cast the value returned by the
*alloc() family is a style issue iff <stdlib.h> has been properly
included. If it hasn't, then the cast represents a very serious
problem. Unfortunately, chances are you won't be warned of it.

Regards,

--
Chris Engebretson - Raytheon STX Corporation | Ph#: (605)594-6829
USGS EROS Data Center, Sioux Falls, SD 57198 | Fax: (605)594-6940
http://edcwww.cr.usgs.gov/ mailto:enge...@sg1.cr.usgs.gov
Opinions are not those of Raytheon Systems Company or the USGS.

Craig Franck

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Derek Harmon <de...@ix.netcom.com> wrote:
>Chris Torek wrote:

>:
>> Whether to cast the return value from malloc() remains a style
>> issue, and, as Mark Brader either said or quoted someone else
>> saying, "while there are plenty of wrong styles, there is no single
>> right style."
>

> Agreed. Although I can't understand why one would choose not to
>cast the return value from malloc() when the only benefit is to warn

>that you didn't include the right headers. I guess I just take including
><stdlib.h> as something you always do before you even call malloc().

I set the compiler to warn me if there's a call to a function with
no prototype. Casting the return value of malloc is unnecessary in
and of itself. I generally look to see if there's a reason to cast
something, rather than cast things for the heck of it. It's reason-
able to expect that a person maintaining your code would know how
void pointers work.

--
Craig
clfr...@worldnet.att.net
Manchester, NH
If you have enough spiritual power to levitate a car, what
difference does it make what religion you call yourself?
-- "Stella"


Aaron Crane

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
In article <35B6C99B...@ix.netcom.com>,
Derek Harmon <de...@ix.netcom.com> writes:

> Aaron Crane wrote:
> > void *hash_lookup (const char *s); /* in a header file */
>
> (Header file being slightly far away, first the interested party has to
> identify which included header hash_lookup belongs to, then they have to
> load that header, then if they want the details mentioned below, they have
> to open the source for the header..)

If you have a generic container ADT, what could it return except a (void *)?
Why should you need to look that up?

> > data = hash_lookup (s);
> >
> > Doesn't that tell you everything you need to know? Is saying
>
> No.. (slight chuckle as I realize we're just on totally different
> wavelengths, but that it's okay).
>
> > data = (struct matrix_type *) hash_lookup (s);
> >
> > *really* any clearer?
>
> Yes. It says hash_lookup returns a struct matrix_type * for keys that s
> must be one of (or possibly NULL, if s is not a key of a struct
> matrix_type and [important] not a key of any other type returnable from
> hash_lookup()).

This is just bizarre. You know what you put into the hash. You get the
same thing out. What is the problem?

> That is, if there are any problems with data at this point, the thing I
> would check immediately is that s is a key for which hash_lookup() should
> return a struct matrix_type *. If hash_lookup() returns a struct complex
> *, then I know either hash_lookup() has logic problems,

How on earth could you possibly tell that hash_lookup is returning a
(struct complex *)?

> or s at this point in the program is somehow screwed up. So I start
> backtracking what's happened to s.
>
> Note the only reason to make hash_lookup() void * is either to evade
> granting hash_lookup() visibility of struct matrix_type,

Not really. You insert an empty `struct matrix_type;' declaration, and
hash_lookup can freely use (but not dereference) pointers to that struct.

> or because hash_lookup() can return pointers to other structs than just
> struct matrix_type. If it only returned one type, then having it return a
> correctly typed pointer would be preferable (and a cast wouldn't be
> necessary, unless it were a downcast).
>
> The only other place to find this sort of information about what a void *
> function returns is in it's implementation. You can hardly condone going
> there to obtain this knowledge, can you?

Again, you know what a (void *) function returns because (for the most part)
you put it there.

> If you didn't have this information at the spot, and in a real-world
> example a dozen things could happen to data in this function, then you'd
> have more work to do (as I said, rooting through hash_lookup() to figure
> out what it returns and when..) With the cast there, you know exactly
> what to look for.
>
> > > That information could be 5, 10, 50 pages somewhere else in the
> > > hardcopy, in a different module.
> >
> > Then that's broken code anyway. If you're declaring something that far
> > from its use, then you deserve everything you get if you simply call it
> > `data'.
>
> Good point, bad identifier name used in that example. But it really isn't
> difficult for a variable to travel that far, in code that takes up seven
> binders on it's own bookshelf. The variable comes to you as a function
> argument, and you don't know it's history.

You have design breakage if you need to use a (void *) argument other than
to store it unaltered or to pass it unaltered to some other function.

> > Both of these are (to my eyes) utterly illegible. Once again, if you
> > can't remember a variable's type for the whole of its scope, then your
> > function is *too long*. (And if you need a local variable name more
> > than two or three characters long, there are too many variables in your
> > function.)
>
> Whoa, first "data" is a bad variable name, now having variable names
> longer than 3 characters is too long? :D I normally do use longer
> variable names than data, obviously, identifiers that better represent
> what they are supposed to be.

I failed to explain myself clearly there. My point is that a global
variable should have a descriptive name, because information about its type
is essentially unavailable when it's used. With local variables and
function arguments, on the other hand, the declaration should be right where
you can see it, and you shouldn't need a long variable name. (That doesn't
in and of itself mean that longer variable names are bad, though.)

> The compiler can't check that the types being assigned are in fact
> compatible, the compiler only exists at compile time. If the example
> void *lookup_hash(char *s) you gave can return 10 different types of
> pointers, dependent on s, then only the caller has any rational ability to
> know what to expect back.

If such a function returns data of several different conceptual types, then
it's encumbent on the designer to write an appropriate wrapper type that can
encompass them all:

enum runtime_type {
RT_TYPE_MATRIX, RT_TYPE_MARKHOV
};

struct runtime_typed_object {
enum runtime_type type;
union {
struct matrix_type *matrix;
struct markhov_type *markhov;
} u;
};

Then you arrange that you only ever put a runtime-typed object into your
hash. Problem solved. Anything else is so prone to breakage as to be
unmaintainable. Look at the way a typical Lisp implementation handles its
objects -- it's essentially the same as this.

> There's not usually a reason for the programmer to lie to himself, or
> other people associated with maintaining his code. If he resorts to using
> a suspicious pointer cast, he should document it explicitly with a cast to
> the type he expects.

If a conversion to or from (void *) is suspicious, then the code has other
problems which should be fixed. If it's not suspicious, then an explicit
cast is pointless clutter.

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Szu-Wen Huang wrote:
> started. I think I may have a better example. Consider a program
> that needs to send various IPC messages, each looking like:
>
#include <stdlib.h>

:
> void SendMessage1(...)
> {
> Message1_t *p;
>
> p = malloc(sizeof *p);
> if (p != NULL)
> {
> p->data1 = ...;
> p->data2 = ...;
> ...
> sysSend(p); /* will also free the buffer for us */
> }
> }
:
> This is quite a candidate to cut and paste to create all the other
> messages, but if you cast malloc, instead of one change you now must
> make two per function. Multiply that by thirty messages, and it's
> sufficiently annoying to make one ask "why cast?"

You would have to make 2 changes without cast, 3 changes with
cast (function name, declaration and cast). Hopefully if your editor
has modern cut and paste facilities, it has templatized cut and paste
facilities, if not try a short Perl script. Besides, code replication
is a dangerous practice: there are always things that must be changed
between renditions. If the code is really useful, there are more than
just 2 or 3 changes that will have to be made. If not, it could be
better generalized than to clone it thirty times.

Derek Harmon

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Szu-Wen Huang wrote:
> Derek Harmon (de...@ix.netcom.com) wrote:
> : himself walked up to a 100,000 line C program with void pointers
> : everywhere and no casts, and you pointed to a void pointer and asked
:
> I disagree. A program where the type of a particular variable isn't
> immediately obvious is already a poor program to start with. Global
> variables should be easy to find, and local variables should be even
> easier to find.

I agree that a program with void pointers all over the place is
already a poor program, made worse off when the authors don't cast them.
Global variables should be easy to find, local variables should be even
easier to find, and a void pointer assignment should be easy to cast.

Of course, that's the theory, the reality is different. :)

> No, I argue for placing variable declarations where they can be found,
> instead of littering type comments all over the place. Are you in
> favor of:

When you are using a void *, just where exactly is the variable
declaration easily found? It could be anywhere. You make the type most
easily found in these circumstances by casting the void *.

> The unnecessary cast masks an error.

That error was not including stdlib.h. If you use malloc(), don't
forget stdlib.h. If you use any function, don't forget its header. I can
understand people like to be notified that they forgot to include stdlib,
but they sacrifice notification of other more devious errors.

I can understand (and have cited the FAQ to support such an
exception) people choosing, for a well-defined and understood function
like malloc, to not cast it. It's a personal choice: since I wouldn't
leave out stdlib.h I would rather make sure I got both sides of the
statement correct, ie,

struct a_t *a;
struct s_t *s;
:
:
a = (struct s_t *)malloc(sizeof struct s_t);

In this (deliberate typo a for s) example I will get a warning
because struct s_t * isn't the type of a, and I meant for this line to
be s. Perhaps earlier I already alloc'd for a, and this would've lost
that earlier alloc. It will draw my attention back and I can fix my
error.

I'd rather have the compiler guard me from the errors I'm not
expecting, than the error I already know about of forgetting to include
stdlib.h.

If that line were in fact,

a = malloc(sizeof struct s_t);

Then I wouldn't learn until much later that, oops, I assigned to
a on that line instead of s.

You can't know what your typos might be. You should ask the
compiler to watch out for them for you. You know you have to include
stdlib.h when using malloc(). Why is it so much more important to
detect that one error you have complete control over?

Even if you opt to leave malloc() uncast, that doesn't lend
support to not casting other void * return values which are often much
seedier than the benign malloc().

> The typecast masks the error, for no gain. This isn't a big deal,

There is gain, you make sure that what the returned type is,
matches what you expect to be assigned to the lhs, or at least you'll
get those warnings. These are the errors I want the compiler to pro-
tect me from, not because I forgot to do something that's step one of
any C program.

> If I were interested in Steve Summit's opinion, I'd indeed read the
> FAQ. I was asking what *you* meant.

If you cast the return pointer type of malloc() from void to
something explicit, then you cover up this all-pervasive error every-
one claims to exist about forgetting to include <stdlib.h>. My point
to the original poster was if you want to be warned about this, then
do not cast malloc.

However, just because you do not cast void *malloc().. doesn't
mean you shouldn't cast other void * returns or assignments. You get
nothing by not casting other void pointers when they mix and mingle.
By casting you get cues, documentation, warnings if you key one side
or the other incorrectly -- and if you don't know what to cast to --
you get a wake up call that you may not know what you're doing.

> It's dangerous to rely on programming habits that are programmer-
> dependent. If I always use only spaces to indent, then indeed a
> line with tabs is suspect. However, how would you comment if I relied
> on such a subtle device for correctness?

I don't rely on what I like to see, I see the complete opposite
almost _all_ the time! :D The original poster was asking about requiring
the casts as a matter of shop policy, if everyone does it then I'm just
saying these are the sorts of benefits.

As for what sort of whitespace you use between your C tokens,
I don't see how that has any effect on program correctness. However,
obviously, the presence of whitespace is important to readability.

> In other words, how would I, as a maintenance programmer who doesn't
> know you personally, decide whether you were "just commenting" or
> simply didn't know what you were doing? If you want to put a comment,
> I believe a comment, not unnecessary code, is the best device.

Typecasts are part of the C language. If there's a typecast
there it should be obvious what it does. It's like commenting the
addition operator.

I will say that typecasts of something unusual should also be
backed up with a comment (lest an overzealous maintainence programmer
comes along and decides that typecast doesn't need to be there), but
how many people do that? In fact, many programmers do neither.

> : That's all said there, in that one line, with that one typecast
> : when seen by any competent C programmer. How many comments would you use?
>
> /* p1 should hold a string */

What happened to "everything put into a file, comments, etc. etc.,
needs to be maintained?" Which is more succinct, that comment or a (char *)
typecast?

> Yes, actually, and I find myself wondering if the programmer knew his/
> her stuff when I see a cast *operator* doing what a comment should.

That's what that operator is for. You just don't use it for that,
I admit I try and avoid situations where I must use it like that because it
usually means I'm doing something a little shaky. But in those situations
its unconscienable to leave it off.

/* p1 should contain a string */
lpszFieldLabel = p1;

Frankly looks less professional than,

/* p1 should contain a string */
lpszFieldLabel = (char *)p1;

or even just,

lpszFieldLabel = (char *)p1;

Because my question on the first snippet is whether the programmer
is really sure of what p1 is (and you can imagine, I've seen the first
snippet without a comment a LOT). You let everyone know you know what p1
is with the typecast. Then it's very obvious right there.

Even the third snippet, one line instead of two, at least expresses
some presumption of knowledge about p1.

> : outright what data's type is. That information could be 5, 10, 50
> : pages somewhere else in the hardcopy, in a different module.
>
> That makes it a stupid program to start with. The inability to place
> variable declarations where they are easily found is not something
> one fixes with "cast comments".

Well the primarily reason to apply pointer casts is to pointers,
and pointers are a vehicle of travelling between functions, modules,
and possibly even processes. So anytime a pointer is used, you face the
real possibility it could end up someplace far away in the source code,
or from multiple possible places in the source code.

You're right that you can't go back and cast comment everything
after the fact, but if you're writing with explicitly casting void
pointers as a policy, then matters are made easier later.



> Also, tools like c2html, which can take you to the declaration at
> the click of a button, makes your entire point moot.

We're not talking about an easily found declaration. We're
talking about every possible declaration that leads to a pointer that
gets passed to this function (with possibly five or ten layers of
functions between them).

If you had the source for memcpy(), could you point and click on
one of its internal variables and see the declarations of every argument
passed to it through the course of a program?

:


> It may also be a genuine bug, covered up by the extraneous cast by
> an idiot programmer. The point isn't whether a good programmer can
> do something safely, but whether you can tell or not.

It probably is a genuine bug, that example wasn't showing use of
a cast to "cover" anything up. It shows a cast as a checkpoint at that
particular position, with very easily identifiable pre and postconditions
that can be identified.

> By the way, have you considered hungarian notation? It does exactly
> what you're talking about, without the danger of hiding bugs from
> your compiler.

I've seen plenty of Hungarian notation alright, and if the
original poster wants to use Hungarian notation that's certainly his
shop's perogative.

However, Hungarian notation doesn't really apply to void pointer
names, which is what I advocate explicitly casting.

> Let's put it this way. Would you assume, in a program you didn't
> write, that a cast is a comment rather than a bug?

I would use the cast as a comment, recognizing the chance that
it could be a bug. Debugging is a truthfinding expedition. You search
backward collecting facts as you go (casts, comments, invariants) and
at some point you should identify a new fact that doesn't agree with
one of the facts you picked up along the way.

Look the two over closely: one is probably the bug.


Derek Harmon

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Szu-Wen Huang wrote:
> Derek Harmon (de...@ix.netcom.com) wrote:
: :

> Do you realize you've been defending your practice with a whole load of
> bad practices? If your cast is useful, it's because:
>
> - idiot (I don't mean you) named the variable
> - idiot declared the variable far away in non-obvious place
> - idiot can't maintain variable's lifetime

Well, that's some (most?) of the software in the world! ;D

So at least if a shop decides to adopt some questionable practices
and justify them for whatever reasons there are within their organization,
if they cast, I'm saying it makes it easier for me to debug. And perhaps
it'd make it harder to introduce the bugs in the first place.

Just because I recommend casting, doesn't mean I don't recommend
all the other good practices to go along with it. :) Casting probably
isn't the one _most_important_ practice such a shop could adopt, but it's
a start.

> If you're so worried about idiots, why ignore the possibility that
> they'll change a type and not go over all 100,000 lines to see where
> it's used? Your helpful casts, at that point, will quietly compile!

If they looked downwind of where they changed that type, and
saw typecasts, then maybe they would've been a bit more leary of affecting
that change. That's a very common bug.

The pointer assignments responsible for such a problem may not
be (often are) in other modules than the one where the presumably innocent
type change was affected. The compiler wasn't going to notice them anyway.

> It appears to me that a comment will do everything you want without
> all the other risks we cite.

The risks cited aren't that convincing, I can live without being
reminded to include stdlib.h to detect typos instead, and I can live
without having diagnostics for the current module to get type checkpoints
globally throughout all modules. All this, of course, assumes the pro-
grammer uses honest typecasts consistently.

Of course, comments are important, I'm not recommending going
without them. That wasn't the original poster's question.

> As opposed to a compile-time error that any real compiler can direct
> you to the very line of?

It can direct you to the line(s) of all such questionable
assignments within its purview, but the idiots who make those mistakes
also ignore suspicious pointer warnings. I've seen them scrolling
down the screen... that only tells me they do these sorts of things
regularly. :D The lack of casts and comments on those lines only
tell me the original programmers didn't know for certain what should
be happening at that point in the program.

What's bad is backtracking from a known problem spot, and
seeing these shady pointer assignments being made, no cast, no
comment, no information. So I have to spend all my time analyzing
why that shady pointer assignment was being made, instead of gleaming
the elegant in-flow information carried by a cast and continuing the
analysis: now with one more fact in my toolbox.

A castless assignment tells you [virtually] nothing. I don't
know how that makes anything easier. It's obviously there, in the code,
so it was decided to ignore the compiler diagnostic when it was first
compiled and then.. oh, well, let's _not_ put a cast there to explain
what we meant. ;)

Derek Harmon

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Chris Engebretson wrote:
> If you don't like to cast "shaky" pointer assignments (by which
> I assume we're talking about conversions between pointers to
> two different object types), then you're not writing legal C.

Writing legal C is a good reason to cast. :)

> |> That's why you need a cast there, _if_ you know what you're
> |> doing.
>

> You need a cast there because the language requires it. Your

Consider,

struct a_little_struct {
int a;
int b;
};
struct a_big_struct {
int a;
int b;
int c;
};

void *vp;
char *cp;
struct a_little_struct *alsp;
struct a_big_struct *absp;
:
/* Everything points to something valid of its type.. */
:
cp = vp;
vp = absp;
alsp = vp;
alsp = absp;

All things commonly left uncast. My opinion is that they should
be cast (except perhaps the assignment to vp or assignments to void pointer,
though the practice of casting them gives a convenient tag for search ops).

> Other than force of habit, I can't understand why one would choose
> *to* cast the return value of *alloc(). It's not a particularly

1. Suppose there exists the possibility your code will be ported
to C++, where casting void pointers is required.

2. Suppose I make the following typo which, without a cast, the
compiler would not complain about, but with it the compiler
dutifully assures that the types on the lhs and rhs are coherent:

struct mat_a *a;
struct mat_s *s;
/* s and a are characters near each other on QWERTY keyboards */
/* a and s also look alike in a small font */
:
a = (struct mat_a *)malloc(sizeof *a);
:
a = (struct mat_s *)malloc(sizeof *s);

This can also occur if I cut and paste this line multiple times,
having to malloc numerous structs, and just forget to change the
a to an s. If you don't see the mistake above, squint hard. :)
That's how hard it is to detect without the typecast.



> "cast habit" was an easy monkey to get off my back after going
> through and fixing the innumerable cast-induced bugs.

Fortunately nowadays, editors are much more versatile and
scripts exist to rip-off rapid changes. As time progresses, these
global changes will be easier to phase in and out.

Derek Harmon

Derek Harmon

unread,
Jul 23, 1998, 3:00:00 AM7/23/98
to
Aaron Crane wrote:
> This is just bizarre. You know what you put into the hash. You get the
> same thing out. What is the problem?

Okay, suppose I have three routines to display a dialog box with
all the information about a car in a DLL. And I have a hash table that has
pointers to all the cars this dealership sells in an ADT utility library,
its already been loaded with the car data from a database.

Now there are three different routines, and there are three types
of cars: Japanese, European and Domestic. For whatever reason, each should
be displayed a little differently.

Now the display Japanese car routine is passed a key and it should
use hash_lookup to retrieve the void pointer to a Japanese car structure
(I recommend casting the void pointer when its received here, but this is
just the sort of thing I've seen left uncast -- obviously it should be
a japanese car pointer, why not say so?).

Each of the other dialog box routines should retrieve and display
the appropriate car. Hopefully they typecast the returns from hash_lookup
themselves.

Now, if I pass the European car routine a key that actually
identifies a Japanese car, I expect to see some garbage on the European
car dialog box, or possibly a UAE.

This all focuses around the void pointer returned by hash_lookup(),
and hopefully there's a typecast there (don't hold it against me that this
scenario is well-described.. real world situations can be a lot messier).
That should tell me, either the key passed in wasn't to a European car,
or possibly hash_lookup() messed up and didn't return a European car but
returned some other car instead (if it turns out to be proven that the
key was in fact good). Or possibly hash_lookup() was loaded incorrectly,
or something else (the hash ADT tried to allocate a new bucket but
malloc() returned NULL and hash_lookup() doesn't have good error
throwing).

My point being, all these things are obvious things to look at and
they all revolve around a typecast returned void pointer which should
succinctly express all of this.

As far as making the code easier to learn, suppose it's your first
day on the job and you have to support this system with only the source
code and a few diagrams. This is the first problem you faced.. would you
rather face it with or without the typecast on that hash_lookup() return?

> How on earth could you possibly tell that hash_lookup is returning a
> (struct complex *)?

Well, in the example I was going with there, I was supposing
that both struct matrix and struct complex were being put in the hash
(for whatever reason, I understand those two don't mathematically make
much sense together, see the car example..) so if its void * wasn't a
matrix *, it must be a complex *. Obviously, once I retrieve things
from the hash I want to use them again, so I have to cast, preferably
putting them in a correctly typed pointer for later use without casting.

> Again, you know what a (void *) function returns because (for the most part)
> you put it there.

That's definately the ideal, why not cast to tell everyone else? :)

> I failed to explain myself clearly there. My point is that a global
> variable should have a descriptive name, because information about its type
> is essentially unavailable when it's used. With local variables and
> function arguments, on the other hand, the declaration should be right where
> you can see it, and you shouldn't need a long variable name. (That doesn't
> in and of itself mean that longer variable names are bad, though.)

Agreed. Some of my examples have used excessively short
identifiers.

> If such a function returns data of several different conceptual types, then
> it's encumbent on the designer to write an appropriate wrapper type that can
> encompass them all:

Good idea. :) Rarely seen in old code. :( A significant chore
to revamp the code you inherit where void pointers were overused to a
better design (and rarely something management will entrust to the
code's maintainers: "You want to make extensive changes? Why? The
code works most of the time. If you make extensive changes the code
could get worse!").

Derek Harmon

John Kugelman

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Derek Harmon wrote:

>
> Szu-Wen Huang wrote:
>
> > Actually, an explicit cast where one isn't needed tells me the
> > programmer isn't confident with C. Extraneous parentheses often
> > (not always) tell me the same thing.
>
> Confidence in C has nothing to do with it. If Dennis Ritchie
> himself walked up to a 100,000 line C program with void pointers
> everywhere and no casts, and you pointed to a void pointer and asked
> him what type it was supposed to be -- he couldn't tell you.

void* pointers are typless and should be used as such. If the pointer
actually has a recognizable type you shouldn't be using void* pointers.
For instance, void* pointers are used for memcpy(). And you're right,
not even Dennis Ritchie could tell me what type the arguments to
memcpy() are, but not because void* pointers are stylistically bad.
It's because these pointers have no particular type. They can have any.

void* pointers are meant for generic use, to reference typeless memory.
If it is possible to identify a unique type for a void* pointer, you
should be using a pointer to that type.

> If there is a cast next to the function returning that void
> pointer -- then you know! He knows! The next ten consultants to be
> charged with maintaining that code know!

Give me an example where a cast would help readability by identifying a
type. You should already know what type you're expecting to receive, so
I see no need for a cast.

Take malloc() for example. When you assign the result of malloc() to a
pointer there is no need to cast it since you should already know the
type of the variable. And if you don't know the type, maybe you
shouldn't. If you change the type of the variable your cast will now be
broken. Where's the benefit?

> > Remember, *everything* you type into source code, including
> > comments or code with mere commentary value, need to be maintained.
>
> Thank you. Exactly why it should be there. Using this argument,
> and your statement about "Confidence in C," one might suspect you would
> argue for "NO COMMENTS!"

Confidence in C does not help if you are not confident that you know the
meaning of the program. Comments help by telling you what the code does
at a glance. I am confident of my abilities in C but I still don't want
to have to divine the meaning of every piece of source code I read. It
can be difficult if not nearly impossible for obscure code. (Ever take
a look at some of the IOCCC programs?)

> The exact opposite, as my other posts in the thread explain.
> How does including stdlib.h when you use malloc() make it hard on the
> machine?
>
> I mean, a lot of people talk about this error. The error isn't
> in the typecast, the error was forgetting to include stdlib.h.

The cast silences a possibly fatal error. You still have not provided
any convincing reason to have the cast.

> > : You should differentiate between casts from void * and casts
> > : from void *malloc()...
> >
> > What does that mean?
>
> Read the FAQ.

Don't be insulting; I'm sure he has, many times. What's the FAQ have to
do with anything?

> > Programming is a deliberate activity, and everything you type into
> > a C source should have a purpose. What is the purpose of using
> > unnecessary casts?
>
> Unnecessary POINTER casts (see subject line).. of which there
> aren't many: you don't have to cast a typed pointer to (void *), that
> would be ridiculous, and you don't have to key in a cast when your
> assigning between pointers of the same type.
>
> All other explicit pointer casts are very useful. That doesn't
> mean you apply them like a drone.. I won't code,
>
> char *s1;
> char *s2;
> :
> s1 = (char *)s2;
>
> That is admittedly inane. However,
>
> void *p1;
> char *s1;
> :
> s1 = (char *)p1;
>
> Is totally appropriate. If I make that assignment, I as programmer
> had better know that p1 is actually pointing to something string-like. I
> want to indicate this to myself six months from now, and my grandchildren
> who'll be maintaining this code decades from now, that I have thoughtfully
> affixed my stamp of approval to this assignment.

I find it a fairly safe assumption that whatever you assign to a
variable should be compatible with the type of that variable. It's an
obvious assumption that p1 must be a (char *) variable since s1 is one.

> Not having a cast there won't make the compiler go into convulsions
> if p1 happens to not point to something string-like because the compiler has
> nothing to say about the dynamic traits of my code. However it is a plain-
> ly evident requirement to anyone reviewing the code that p1 has an implied
> precondition that it must point to something string-like, and that that
> section of code must not be executed when p1 doesn't meet this precondition.
> Any such execution would plainly be an error.

That's akin to writing

/* p1 must be a string. */

If the comment is correct it's unnecessary, but if it's not it's just
misleading.

> That's all said there, in that one line, with that one typecast
> when seen by any competent C programmer. How many comments would you use?

The same thing is equally obvious without the cast.

> > I don't see how the presence or absence of casting affects the
> > learning curve. If anything, extraneous casting simply clutters
> > the code with non-functional tokens.
>
> data = result;
>
> Does THAT tell you anything?

No, but it would if it were part of some larger body of code.

> Okay, how about,
>
> data = (struct matrix_type *)result;
>
> This line tells you plenty more information about what
> limitations apply to result in this segment of code, and it tells you
> outright what data's type is. That information could be 5, 10, 50
> pages somewhere else in the hardcopy, in a different module.

If result must be a struct matrix_type *, why use a void* pointer? Just
define it as

struct matrix_type *result;

Your example is too obviously contrived.

> What about this,
>
> pResultMat = processLeontiefMat( &retrievedMat, 10);
>
> Not bad, but how much clearer is this to someone just hired
> to learn this code,
>
> pResultMat = processLeontiefMat( (struct markhov_type *)&retrievedMat, 10);

That's some of the stupidest coding I've seen. Do you also cast your
non-pointer variables every few lines to remind you what types they
have?

--
John Kugelman. kuge...@mnsinc.com

I believe we can change anything.
I believe in my dream.
- Joe Satriani

Derek Harmon

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
John Kugelman wrote:
> Derek Harmon wrote:
> > Szu-Wen Huang wrote:
: : :
[many points already whose pros and cons have been covered snipped]
:

> void* pointers are typless and should be used as such. If the pointer
> actually has a recognizable type you shouldn't be using void* pointers.

struct real { /* blah */ };
struct imaginary { /* blah */ };
/* real and imaginary have one member in common that we want to */
/* print. */
:
void *getSquareRootDontCareSign(int arg);
void printRealResult(struct real *prarg);
void printImagResult(struct imaginary *piarg);
:
if (squareRootArg < 0)
printRealResult( (struct real *)getSquareRootDontCareSign( squareRootArg));
else
printImagResult( (struct imaginary *)getSquareRootDontCareSign( squareRootArg));
:

I know programmers (not me personally because I often admonish the
overuse of void pointers) who would argue why declare two pointer variables
to hold pointers of each type, and why assign to those variables before
invoking the print functions. This solution may still be superior in
efficiency if there is verifiable proof allowing the identification of
type, and 30 different return types of the void *getWhatever() function.

I have seen instances of this sort of programming, and how easily
it can become broken when, suppose:

Instead of just returning struct real * for positive arguments,
a getWhatever() function is suddenly rewritten to return either
struct real * or struct linear *.

Before implementing such a change, any wise programmer would go
back and check all the calls to getWhatever() to make sure this doesn't
break anything. If he sees people relying on struct real * typecasts,
then he'll be made aware. If he sees it uncast, either he needs to
study that code longer than he normally would have to, or he'll just
assume it's alright because if the writer of the calling function cared,
they would've typecast the void * return.

> void* pointers are meant for generic use, to reference typeless memory.
> If it is possible to identify a unique type for a void* pointer, you
> should be using a pointer to that type.

You certainly could be using a pointer to that type. Whether you
should or not is almost certainly a whole other debate.. :) I happen to
agree with you on that; but if you are handing void pointers around, you
should be casting them.

And just to reiterate, I do not mean something ridiculous like,

vp1 = (void *)vp2; /* both void pointers */

but rather always,

ip = (int *)vp1;

and as a stylistic issue in which you may want to locate everyplace
you initialized a void pointer later (particularly if this void pointer will
be set back to a typed pointer in the future),

vp2 = (void *)ip;

The last point is of less significance. The second will cause the
compiler to grumble if uncast, but let me say that programmers have been
known to ignore that grumble. Certainly, if the question is asked whether
to typecast void pointers or not, this must be the predisposition (exception
of leaving malloc() uncast acknowledged everywhere right back to the second
sentence of my original reply.)

(It was so "important" I acknowledged its plausible exception from
the second sentence of my original reply, the one with the controversial
non-segueway.)

(Though I will continue to cast the return of malloc() for the
reasons I've cited.)

> Give me an example where a cast would help readability by identifying a
> type. You should already know what type you're expecting to receive, so
> I see no need for a cast.

You should already know _at_ the time of writing. What if you
forget years later? What if you get promoted when your company gets
spun-off, and yoour old code gets inherited by some kid straight out of
college? What if somebody, not noticing a cast there, decides to change
the types you may receive?

> > I mean, a lot of people talk about this error. The error isn't
> > in the typecast, the error was forgetting to include stdlib.h.
>
> The cast silences a possibly fatal error. You still have not provided
> any convincing reason to have the cast.

To insure the type receiving the assignment is the type being
assigned, otherwise the compiler will complain. This is the main purpose
of casts. I would prefer the compiler to check my typos (which occur
randomly and I have little control over, other than to type really slow
and with great forethought, which isn't acceptable since a good incremental
compiler can tell me my errors within minutes [usually less]) than whether
I'm becoming absent-minded (I personally still trust my memory, and trust
that if I'm using malloc(), I've included stdlib.h, so I believe this is
something I have control over).

> Don't be insulting; I'm sure he has, many times. What's the FAQ have to
> do with anything?

I admit that was a short biting comment, more influenced by the
passions of the time and his criticism of my "thesis" (or rather support
of it in the first paragraph of the post in question). I'm very easy-
going as to what people choose to cast and not to cast, I'm just sharing
what I know. However, earlier in my original reply (to which that was a
reply to his reply) I believe I had made clear that the FAQ explains a
reason for not casting the void pointer returned by malloc(). It was
my feeling that if he had read my original post up to that point (including
in particular the first "thesis" paragraph) then the difference between
typecasting void pointers and typecasting the return of malloc() was
already established.

> > void *p1;
> > char *s1;
> > :
> > s1 = (char *)p1;

:


> I find it a fairly safe assumption that whatever you assign to a
> variable should be compatible with the type of that variable. It's an
> obvious assumption that p1 must be a (char *) variable since s1 is one.

That's because you can see them close to their declarations.
If you're out in the middle of the woods (in some function in a big
program) that have received arguments from anywhere in the world (the
big program) then it's a less safe assumption.



> The same thing is equally obvious without the cast.

Is it more or less obvious [with the cast]? I think it is more
obvious with a cast. Readability is about making things more obvious.

Can I analogize using typecasts to putting on a coat? It's 50
deg F out (wishful thinking where I am now). :) If I put on a coat,
am I warmer or as warm?

Technically, I am as warm. My body temperature, hopefully,
will not exceed normal limits. But the coat makes it easier to keep
the warmth in, rather than loose it. Still, my body is a competent
engine for maintaining temperature (I know everything about the code
in front of me). So do you want the coat or don't you? It's not like
you're dying of hypothermia (going to forget about what two uncast
pointers are).. not when it's just forty degrees.

Suppose it gets colder though (akin to going into a bigger and
bigger program where you can see less and less of the entire process
at a time)? Now the risk of dying of hypothermia (having a bug pop
up from a bad code change that you didn't expect somebody else to
make) is greater. Do you want the coat?

> That's some of the stupidest coding I've seen. Do you also cast your
> non-pointer variables every few lines to remind you what types they
> have?

The point was casting void pointers, so the function was contrived
to take a void pointer. If it takes a half dozen different types, I want
to cast them going in. When they come out, I want to cast them too. If
I don't know what to cast them to, I'm in trouble. A better example was
the hash_lookup() elsewhere in this thread.

Derek Harmon

Szu-Wen Huang

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
[...]
: You would have to make 2 changes without cast, 3 changes with

: cast (function name, declaration and cast). Hopefully if your editor
: has modern cut and paste facilities, it has templatized cut and paste
: facilities, if not try a short Perl script.

I fear you miss the point. Your assertion of casting is not better
supported by suggesting how to minimize my extra work. What you
need to do is to convince people that the extra effort is worthwhile
(in which case I'd even write code generators to do it!).

Right now I don't have to "templatized cut", nor write Perl scripts.
What good does it do me to cast and invite work?

: Besides, code replication


: is a dangerous practice: there are always things that must be changed
: between renditions. If the code is really useful, there are more than
: just 2 or 3 changes that will have to be made.

Which is even more reason to minimize things that need to be changed.

: If not, it could be


: better generalized than to clone it thirty times.

The alternative to this would be a pretty large switch statement,
which isn't exactly a dream either.

Szu-Wen Huang

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
: Szu-Wen Huang wrote:
[...]
: I agree that a program with void pointers all over the place is

: already a poor program, made worse off when the authors don't cast them.
: Global variables should be easy to find, local variables should be even
: easier to find, and a void pointer assignment should be easy to cast.

: Of course, that's the theory, the reality is different. :)

Yes, indeed. Reality is that when a programmer is unable to make
variables easy to figure out, the programmer is even less likely to
know how to cast properly. I find myself unlikely to trust the
casts of a programmer who doesn't know where to declare variables.

[...]
: When you are using a void *, just where exactly is the variable


: declaration easily found? It could be anywhere. You make the type most
: easily found in these circumstances by casting the void *.

When you write:

p = malloc(...);

the type of malloc() is unimportant, because the compiler makes sure
that it's compatible with the type of p. IOW, all you need to do is to
look up how p is declared.

: > The unnecessary cast masks an error.

: That error was not including stdlib.h. If you use malloc(), don't
: forget stdlib.h. If you use any function, don't forget its header. I can
: understand people like to be notified that they forgot to include stdlib,
: but they sacrifice notification of other more devious errors.

Your casts cause no notification to occur.

: I can understand (and have cited the FAQ to support such an


: exception) people choosing, for a well-defined and understood function
: like malloc, to not cast it. It's a personal choice: since I wouldn't
: leave out stdlib.h I would rather make sure I got both sides of the
: statement correct, ie,

: struct a_t *a;
: struct s_t *s;
: :
: :
: a = (struct s_t *)malloc(sizeof struct s_t);

: In this (deliberate typo a for s) example I will get a warning
: because struct s_t * isn't the type of a, and I meant for this line to
: be s. Perhaps earlier I already alloc'd for a, and this would've lost
: that earlier alloc. It will draw my attention back and I can fix my
: error.

Except if you had written:

a = malloc(sizeof *a);

you wouldn't have either problem.

[...]
: You can't know what your typos might be. You should ask the


: compiler to watch out for them for you. You know you have to include
: stdlib.h when using malloc(). Why is it so much more important to
: detect that one error you have complete control over?

Nobody, as far as I can tell, is trying to tell you that not casting
malloc() is important. People are trying to tell you that casting
malloc() may hide an error, for no appreciable gain. Therefore,
somebody who casts malloc() (indeed anybody who advocates writing
things the language does not require) should present a good reason
to do so.

Comments are unnecessary, as far as the language is concerned.
However, they demonstrably improve aspects of the program, so
they are considered desirable. I'm afraid you haven't shown the
same of unnecessary casting.

[...]
: > The typecast masks the error, for no gain. This isn't a big deal,

: There is gain, you make sure that what the returned type is,
: matches what you expect to be assigned to the lhs, or at least you'll
: get those warnings. These are the errors I want the compiler to pro-
: tect me from, not because I forgot to do something that's step one of
: any C program.

The example above shows that there's no gain.

[...]
: However, just because you do not cast void *malloc().. doesn't


: mean you shouldn't cast other void * returns or assignments. You get
: nothing by not casting other void pointers when they mix and mingle.
: By casting you get cues, documentation, warnings if you key one side
: or the other incorrectly -- and if you don't know what to cast to --
: you get a wake up call that you may not know what you're doing.

1. To know the type of the right-hand side, look up the left-hand
side, for the compiler guarantees they are compatible.
2. To document, use a comment.
3. If the RHS changes type during maintenance, casting will shut
the compiler up, while not casting generates a compile-time error.
4. If the LHS changes type, p = malloc(sizeof *p) automatically
adjusts.

[...]
: As for what sort of whitespace you use between your C tokens,


: I don't see how that has any effect on program correctness. However,
: obviously, the presence of whitespace is important to readability.

Oh, for one it tells me that somebody's been there. :)

: > In other words, how would I, as a maintenance programmer who doesn't


: > know you personally, decide whether you were "just commenting" or
: > simply didn't know what you were doing? If you want to put a comment,
: > I believe a comment, not unnecessary code, is the best device.

: Typecasts are part of the C language. If there's a typecast
: there it should be obvious what it does. It's like commenting the
: addition operator.

True, but a typecast operator is dubious by *nature*. Its purpose to
the language and compiler is to override the default typechecking
mechanism. That's why it's a bad thing to sprinkle unnecessarily.

: > /* p1 should hold a string */

: What happened to "everything put into a file, comments, etc. etc.,
: needs to be maintained?" Which is more succinct, that comment or a (char *)
: typecast?

There's a difference. A wrong comment is considerably less dangerous
than a wrong cast. Both are confusing, but the latter disables type-
checking. That's why I said, "if you have to, use a comment."

: > Yes, actually, and I find myself wondering if the programmer knew his/


: > her stuff when I see a cast *operator* doing what a comment should.

: That's what that operator is for.

No, it isn't. The cast operator is essential to the C language, but
cast comments are not.

[...examples...]
: Even the third snippet, one line instead of two, at least expresses


: some presumption of knowledge about p1.

In other words, we read opposite things from the same code. That
means to me that neither is a good idea. :)

[...]

Szu-Wen Huang

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
: Szu-Wen Huang wrote:
[...]
: > If you're so worried about idiots, why ignore the possibility that

: > they'll change a type and not go over all 100,000 lines to see where
: > it's used? Your helpful casts, at that point, will quietly compile!

: If they looked downwind of where they changed that type, and
: saw typecasts, then maybe they would've been a bit more leary of affecting
: that change. That's a very common bug.

If they *don't* look downwind, like inexperienced programmers do by
definition, then the explicit cast will compile without error and
end up as a more expensive run-time error.

[...]
: > It appears to me that a comment will do everything you want without


: > all the other risks we cite.

: The risks cited aren't that convincing, I can live without being
: reminded to include stdlib.h to detect typos instead, and I can live
: without having diagnostics for the current module to get type checkpoints
: globally throughout all modules. All this, of course, assumes the pro-
: grammer uses honest typecasts consistently.

Point is, what for? There are small dangers to using unnecessary
casts, and minimal gains, if any. There exist alternative methods
to achieve the commentary value without the dangers.

[...]
: > As opposed to a compile-time error that any real compiler can direct


: > you to the very line of?

: It can direct you to the line(s) of all such questionable
: assignments within its purview, but the idiots who make those mistakes
: also ignore suspicious pointer warnings. I've seen them scrolling
: down the screen... that only tells me they do these sorts of things
: regularly. :D

You can't keep going back to the idiot programmer defense to invalidate
the value of such warnings to average and experienced programmers.

[...]
: What's bad is backtracking from a known problem spot, and


: seeing these shady pointer assignments being made, no cast, no
: comment, no information. So I have to spend all my time analyzing
: why that shady pointer assignment was being made, instead of gleaming
: the elegant in-flow information carried by a cast and continuing the
: analysis: now with one more fact in my toolbox.

Debugging is always easier if you can assume that a smart programmer
wrote it. However, without the guarantee that the cast is actually
an "elegant in-flow information", your analysis is just as hard, if
not harder.

: A castless assignment tells you [virtually] nothing. I don't


: know how that makes anything easier. It's obviously there, in the code,
: so it was decided to ignore the compiler diagnostic when it was first
: compiled and then.. oh, well, let's _not_ put a cast there to explain
: what we meant. ;)

Comment. (And please don't say an idiot would not comment. An
idiot would not cast-comment either.)

Derek Harmon

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Szu-Wen Huang wrote:
> : When you are using a void *, just where exactly is the variable
> : declaration easily found? It could be anywhere. You make the type most
> : easily found in these circumstances by casting the void *.
>
> When you write:
>
> p = malloc(...);
>
> the type of malloc() is unimportant, because the compiler makes sure
> that it's compatible with the type of p. IOW, all you need to do is to
> look up how p is declared.

What about all the other places you're using a void *? :)
Often, a void pointer is passed into a function and it's much harder
than just looking up a nearby declared type to identify it.

> : forget stdlib.h. If you use any function, don't forget its header. I can
> : understand people like to be notified that they forgot to include stdlib,
> : but they sacrifice notification of other more devious errors.
>
> Your casts cause no notification to occur.

The example in the following two paragraphs explain the notifications
that can occur.

> : I can understand (and have cited the FAQ to support such an
> : exception) people choosing, for a well-defined and understood function
> : like malloc, to not cast it. It's a personal choice: since I wouldn't
> : leave out stdlib.h I would rather make sure I got both sides of the
> : statement correct, ie,
>
> : struct a_t *a;
> : struct s_t *s;
> : :
> : :
> : a = (struct s_t *)malloc(sizeof struct s_t);
>
> : In this (deliberate typo a for s) example I will get a warning
> : because struct s_t * isn't the type of a, and I meant for this line to
> : be s. Perhaps earlier I already alloc'd for a, and this would've lost
> : that earlier alloc. It will draw my attention back and I can fix my
> : error.
>
> Except if you had written:
>
> a = malloc(sizeof *a);
>
> you wouldn't have either problem.

Except if I had "typed it correctly" you mean (in your preferred
format, which is an acceptable style choice, but can be just as easily
mistyped):

s = malloc(sizeof *a);

or

a = malloc(sizeof *s);

No warning, and just as hard to distinguish on my terminal (eyes
could use a larger font). The protection is against TYPOS, you don't
know when they are going to happen. You do know when you use malloc(),
so you know to include stdlib.h. A typo can hit anywhere.

This nefarious malloc() typo is also well known to strike in
replicated code! You do some malloc's in one function and want to cut
them and paste them somewhere else, but you don't change all the
identifiers, you forget one (hey, if you can forget stdlib.h, you
can forget to type something). It can happen to you.

I can type several times faster if I don't worry about getting
each keypress perfect, with incremental compilers that are very good at
locating typos (unless you don't give them the information they need),
able to locate thousands of typos per second.. why slow myself down?

Back in the days of punchcards, not making a syntax error was
important - I agree, but now compiling is cheap (and incremental compiling
a module of a much larger project is cheap.. rebuilding ain't cheap, but..)

> The example above shows that there's no gain.

Your example neglects to show the error. Of course there's no
gain when you don't make the error its supposed to protect you against.
If you leave malloc() uncast, and you include stdlib.h, then guess what?
There's no gain to that either. :)

Typos occur stochastically, including stdlib.h only occurs once
in a well defined place for well defined conditions.

> 1. To know the type of the right-hand side, look up the left-hand
> side, for the compiler guarantees they are compatible.

If it's a void pointer the compiler can only say its suspicious.

> 2. To document, use a comment.

It's so un-C like to use a comment to document something C
itself includes an operator for. Something that you can search on,
that looks the same everyplace in the code. Ever try searching on
a comment? You can find every (void *) in a program if you cast
them in that manner, do you really want to put comments on all those
lines? And then what do you search for? You could make up a common
tag, and standardize it in your organization, but the C language
already does that.

You've claimed typecasting these pointer operations reveals a
lack of competence in and confidence with C. When C itself offers a
syntactic unit which can be used to express ("document/guarantee/warn
me if not so/the types on both sides are this or compatible to this")
elegantly and fluently in the C language.. you claim it shouldn't be
used?

> 3. If the RHS changes type during maintenance, casting will shut
> the compiler up, while not casting generates a compile-time error.

If you didn't cast originally, then either its not a void pointer
assignment or you ignored the compile-time error then. If the RHS is a
void pointer, its type could change during maintainence in any other
module that the one being compiled.

> 4. If the LHS changes type, p = malloc(sizeof *p) automatically
> adjusts.

Agreed. That was never contested. This has nothing to do with
protecting against typos (because the line doesn't contain a typo).

> True, but a typecast operator is dubious by *nature*. Its purpose to
> the language and compiler is to override the default typechecking
> mechanism. That's why it's a bad thing to sprinkle unnecessarily.

It does that, it also documents that you wanted to override the
default typechecking mechanism, and it prominently places into view the
required type at that point in the program.

> : > Yes, actually, and I find myself wondering if the programmer knew his/
> : > her stuff when I see a cast *operator* doing what a comment should.
>
> : That's what that operator is for.
>
> No, it isn't. The cast operator is essential to the C language, but
> cast comments are not.

The cast operator is essential for what? Casting void pointers?


Derek Harmon

(Okay, sometimes you cast an int to a float to force a float division
instead of an int division; but I like ending the post on that question..)

Aaron Crane

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
In article <35B91323...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> It's so un-C like to use a comment to document something C itself includes
> an operator for.

How many times? A cast is not a documentation operator; it's a place where
you lie to the compiler.

> You've claimed typecasting these pointer operations reveals a lack of
> competence in and confidence with C. When C itself offers a syntactic
> unit which can be used to express ("document/guarantee/warn me if not
> so/the types on both sides are this or compatible to this") elegantly and
> fluently in the C language.. you claim it shouldn't be used?

The cast explicitly says `I know that this construct is unwise, defeats
static type-checking, and may cause undefined behaviour, but do it anyway'.
It emphatically does not give you a guarantee that both sides of an
assignment are compatible -- that's what happens if you omit the cast.

At the start of this thread, I assumed in the absence of evidence to the
contrary that you are a reasonably proficient C programmer. I'm now
beginning to wonder whether that's the case, as this makes at least three
rather fundamental errors in your prose and your code. (I won't go into
them now, but I have the references if you want me to back up that
assertion.)

> The cast operator is essential for what? Casting void pointers?

No, it's essential for things like converting a pointer to one structure
type to a pointer to another type -- which is occasionally a reasonable
thing to do. It's not essential for casting to or from a void pointer.

> (Okay, sometimes you cast an int to a float to force a float division
> instead of an int division; but I like ending the post on that question..)

Oh, so you're resorting to cheap rhetorical tricks instead of actually
having a sensible argument?

Derek Harmon

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
Aaron Crane wrote:
> In article <35B91323...@ix.netcom.com>,
> Derek Harmon <de...@ix.netcom.com> writes:
> > It's so un-C like to use a comment to document something C itself includes
> > an operator for.
>
> How many times? A cast is not a documentation operator; it's a place where
> you lie to the compiler.

It might be a place where _you_ lie to the compiler. It may be
a place where other programmers lie to the compiler. In that case, its
lying to the compiler that the problem. It can be used as a form of
documentation, and while it has no ability to force the original pro-
grammer's will (we'll assume he applied the cast knowledgeably about
all the requirements up to that points and the expected requirements
of what he is casting) upon subsequent generations -- if they were
brought up to respect the cast rather than regard it deceptively,
it would be very effective in this capacity.

> The cast explicitly says `I know that this construct is unwise, defeats
> static type-checking, and may cause undefined behaviour, but do it anyway'.

Both of us have had our ears pressed up against the monitor
in our development environments trying too hard to listen to what
programming language constructs are saying to us. ;) That hiss?
Those aren't words, that's the electron beam gun scanning across the
rows of phosphor on the inside of the monitor. :D

> It emphatically does not give you a guarantee that both sides of an
> assignment are compatible -- that's what happens if you omit the cast.

C has no programming by contract, with C there aren't many
guarantees at all. If you want to have these benefits you have to
learn to read these properties into the source. They have to be
applied consistently to the code, and above all, honestly (none of
this lying to the compiler).

Casts don't tell the truth or lies, the humans that use them do.

Omitting the cast of a void pointer guarantees nothing, only
that the programmer is crazy.

> > The cast operator is essential for what? Casting void pointers?
>

> No, it's essential for things like converting a pointer to one structure
> type to a pointer to another type -- which is occasionally a reasonable
> thing to do.

Exactly.

> It's not essential for casting to or from a void pointer.

Well if you're willing to ignore compiler warnings for not
casting void pointers, then why waste time typing the cast between
structures? Just ignore those warnings too.

As for other dangerous assignments, would you really trust
something like,

long int *lip;
int *ip;
:
ip = lip;

without a cast? What if you index them later? You might pass
ip to another function and forget about it.. time passes, programmers
forget little details in other functions, change something somewhere
else, go boom.

> > (Okay, sometimes you cast an int to a float to force a float division
> > instead of an int division; but I like ending the post on that question..)
>

> Oh, so you're resorting to cheap rhetorical tricks instead of actually
> having a sensible argument?

I don't expect to win, I've seen lots of code left uncast.. and
I will continue to see code left uncast, its not a major issue for me. :)
I don't even like void pointers.

As for the rhetorical trick, I just thought it climactic. But,
in the interest of fairness, I included the main use of a nonpointer cast
in at the bottom (not that it really pertains to the "debate").

Derek Harmon

Aaron Crane

unread,
Jul 24, 1998, 3:00:00 AM7/24/98
to
In article <35B94F03...@ix.netcom.com>,
Derek Harmon <de...@ix.netcom.com> writes:

> Aaron Crane wrote:
> > How many times? A cast is not a documentation operator; it's a place where
> > you lie to the compiler.
>
> It might be a place where _you_ lie to the compiler. It may be a place
> where other programmers lie to the compiler.

It is designed for lying to the compiler. If you use it for something else
(`documentation') then your understanding of C has a fundamental error.

> In that case, its lying to the compiler that the problem.

Occasionally, the programmer knows more about what is going on than the
compiler; in these circumstances, a cast is appropriate, and it is required
if the code would otherwise generate a diagnostic. Otherwise, a cast adds
*nothing* that is not better placed in the comments.

> It can be used as a form of documentation,

But only if you don't know what it's really used for. See above.

> and while it has no ability to force the original programmer's will (we'll


> assume he applied the cast knowledgeably about all the requirements up to
> that points and the expected requirements of what he is casting)

Much of your argument for explicit casts on (void *) values has been based
on the `idiot programmer' concept, yet now you assume that programmers apply
casts knowledgeably? I find that, on the contrary, the vast majority of
explicit casts I have seen in production code represent misconceptions or
poor design on behalf of the programmer.

> > The cast explicitly says `I know that this construct is unwise, defeats
> > static type-checking, and may cause undefined behaviour, but do it anyway'.

> > It emphatically does not give you a guarantee that both sides of an
> > assignment are compatible -- that's what happens if you omit the cast.
>
> C has no programming by contract, with C there aren't many guarantees at
> all.

On the contrary, you are guaranteed that if you perform an uncast assignment
between incompatible types, you will get a diagnostic; at this point, the
implementation is free to stop translating.

Go read the standard; you apparently have no idea what you're talking about.

> If you want to have these benefits you have to learn to read these
> properties into the source.

No, you have to know what the language does and does not allow.

> They have to be applied consistently to the code, and above all, honestly
> (none of this lying to the compiler).
>
> Casts don't tell the truth or lies, the humans that use them do.

I assume that this statement is an analogy to the argument that `guns don't
kill people, people do'. You are saying that casts are not in themselves
bad, just as guns and bullets are not in themselves bad. But just as you
can't shoot someone without a weapon, you can't lie about your data without
a cast or some related construct. So why are you advocating casts in places
where the language doesn't require them?

> Omitting the cast of a void pointer guarantees nothing, only that the
> programmer is crazy.

The programmer is crazy to allow the compiler to perform static type
checking? Do you realise how stupid that statement is?

> Well if you're willing to ignore compiler warnings for not casting void
> pointers, then why waste time typing the cast between structures? Just
> ignore those warnings too.

Let's take a concrete (though obviously contrived) example:

int *ip;
char *cp;
void *vp;

/* elide code which initialises cp */
vp = cp; /* (1) */
ip = cp; /* (2) */

Here you are guaranteed (6.3.16.1) that the expression labelled (1) need not
generate a diagnostic, as a pointer to an object is assigned to a pointer to
void, both of which have the same qualifiers. But the expression labelled
(2) requires a diagnostic, as it violates the third constraint listed in
6.3.16.1. Since it requires a diagnostic, the compiler may stop translating
at this point.

If you still can't see the distinction, why not give up on C, which is
clearly too difficult for you?

> As for other dangerous assignments, would you really trust something like,
>
> long int *lip;
> int *ip;
> :
> ip = lip;
>
> without a cast?

Of course not. But if I saw a cast, I wouldn't assume that the code was
correct without some external indication -- like a comment. Even then, I'd
want to know why neither of these pointers is a (void *), since the
programmer is evidently lying about data types.

> What if you index them later? You might pass ip to another function and
> forget about it.. time passes, programmers forget little details in other
> functions, change something somewhere else, go boom.

Yes. And without a cast, you get a diagnostic every time you translate that
code, but with a cast, the compiler assumes you know best.

> I don't expect to win,

You can't win a technical discussion with no understanding of the issues
involved, so this statement doesn't surprise me.

Derek, you seem like a nice enough person, even if you are astoundingly
misguided on this topic. If you give up now, I shouldn't think anyone will
hold it against you. But I must say that *plonk*ing is perilously close as
far as I'm concerned.

John Kugelman

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Derek Harmon wrote:
>
> Szu-Wen Huang wrote:
> > : When you are using a void *, just where exactly is the variable
> > : declaration easily found? It could be anywhere. You make the type most
> > : easily found in these circumstances by casting the void *.
> >
> > When you write:
> >
> > p = malloc(...);
> >
> > the type of malloc() is unimportant, because the compiler makes sure
> > that it's compatible with the type of p. IOW, all you need to do is to
> > look up how p is declared.
>
> What about all the other places you're using a void *? :)
> Often, a void pointer is passed into a function and it's much harder
> than just looking up a nearby declared type to identify it.

You're proving tiring. Show me of an example where a void* pointer is
used that has an identifiable type but cannot be replaced by that type.
You are making me believe that you don't really understand the purpose
of void* pointers, which is to hold generic, typeless data. void* HAS
NO TYPE. If your pointer, don't use void*.

> > Except if you had written:
> >
> > a = malloc(sizeof *a);
> >
> > you wouldn't have either problem.
>
> Except if I had "typed it correctly" you mean (in your preferred
> format, which is an acceptable style choice, but can be just as easily
> mistyped):
>
> s = malloc(sizeof *a);
>
> or
>
> a = malloc(sizeof *s);
>
> No warning, and just as hard to distinguish on my terminal (eyes
> could use a larger font). The protection is against TYPOS, you don't
> know when they are going to happen. You do know when you use malloc(),
> so you know to include stdlib.h. A typo can hit anywhere.

You could easily make a typo in the cast, too. Besides, you need to use
better variables names if a simple typo references another variable.

If you can't find that typo within ten seconds you don't deserve to be
programming in C. "Protecting against typos" is a stupid reason to add
a cast. You should add a cast if you need its semantics in your code
(which may be never in strictly conforming code), not to protect
yourself.

>
> This nefarious malloc() typo is also well known to strike in
> replicated code! You do some malloc's in one function and want to cut
> them and paste them somewhere else, but you don't change all the
> identifiers, you forget one (hey, if you can forget stdlib.h, you
> can forget to type something). It can happen to you.

Shouldn't you been using templatized cut and paste or a Perl script for
this?

> I can type several times faster if I don't worry about getting
> each keypress perfect, with incremental compilers that are very good at
> locating typos (unless you don't give them the information they need),
> able to locate thousands of typos per second.. why slow myself down?

Coding speed has *no* relation to typing speed. If you find yourself
typing 90 WAM you need to slow down and think some more.

> > The example above shows that there's no gain.
>
> Your example neglects to show the error. Of course there's no
> gain when you don't make the error its supposed to protect you against.
> If you leave malloc() uncast, and you include stdlib.h, then guess what?
> There's no gain to that either. :)

You don't clutter your source with unnecessary code.

> > 1. To know the type of the right-hand side, look up the left-hand
> > side, for the compiler guarantees they are compatible.
>
> If it's a void pointer the compiler can only say its suspicious.

It's only suspicious if you screwed up. Don't do that.

> > 2. To document, use a comment.
>
> It's so un-C like to use a comment to document something C
> itself includes an operator for. Something that you can search on,
> that looks the same everyplace in the code. Ever try searching on
> a comment? You can find every (void *) in a program if you cast
> them in that manner, do you really want to put comments on all those
> lines? And then what do you search for? You could make up a common
> tag, and standardize it in your organization, but the C language
> already does that.

That's not what a cast operator is for! Get that through your head. A
cast is used to change the type of an object or value to another, not to
help document your code. That's what *comments* are for. It's stupid
to use a cast to document something that should be better placed inside
a comment.

The only situations I can think of for using a cast in strictly
conforming code is when converting the type of a value to another type
without any intermediate variables. For example, casting NULL to the
appropriate type in a varargs function.

> You've claimed typecasting these pointer operations reveals a
> lack of competence in and confidence with C. When C itself offers a
> syntactic unit which can be used to express ("document/guarantee/warn
> me if not so/the types on both sides are this or compatible to this")
> elegantly and fluently in the C language.. you claim it shouldn't be
> used?

That's not its purpose of a cast. And you seem to misunderstand the
purpose of void* pointers, too.

> > 4. If the LHS changes type, p = malloc(sizeof *p) automatically
> > adjusts.
>
> Agreed. That was never contested. This has nothing to do with
> protecting against typos (because the line doesn't contain a typo).

Again, using casts to protect against typos is a stupid practice at
best. You should rethink some of your design issues if you feel you
need casts to produce warnings about typos. For one, get a better
variable naming system. I have never yet made a typo which silently
referred to another variable (and also did not cause some sort of
compile warning or error).

> > : > Yes, actually, and I find myself wondering if the programmer knew his/
> > : > her stuff when I see a cast *operator* doing what a comment should.
> >
> > : That's what that operator is for.
> >
> > No, it isn't. The cast operator is essential to the C language, but
> > cast comments are not.
>
> The cast operator is essential for what? Casting void pointers?

No, it's usually useful for doing non-conforming things, such as
converting between incompatible types. It can also be useful in
strictly conforming code, but that's not the usual thing.

> (Okay, sometimes you cast an int to a float to force a float division
> instead of an int division; but I like ending the post on that question..)

Yes, that's a legitimate use of a cast. Preventing typos is not.

John Kugelman

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Derek Harmon wrote:

>
> Aaron Crane wrote:
> >
> > The cast explicitly says `I know that this construct is unwise, defeats
> > static type-checking, and may cause undefined behaviour, but do it anyway'.
>
> Both of us have had our ears pressed up against the monitor
> in our development environments trying too hard to listen to what
> programming language constructs are saying to us. ;) That hiss?
> Those aren't words, that's the electron beam gun scanning across the
> rows of phosphor on the inside of the monitor. :D

Could you at least try to stay relevant?

> > It emphatically does not give you a guarantee that both sides of an
> > assignment are compatible -- that's what happens if you omit the cast.
>
> C has no programming by contract, with C there aren't many

> guarantees at all. If you want to have these benefits you have to
> learn to read these properties into the source. They have to be


> applied consistently to the code, and above all, honestly (none of
> this lying to the compiler).

What the hell are you rambling about now? If the two sides of an
assignment aren't compatible a compiler is free to stop translation. By
adding a cast, you force the sides to be compatible, but whether or not
an incompatible type can be converted to a compatible one depends on the
system.

> Casts don't tell the truth or lies, the humans that use them do.

I have no idea what that means.

> > > The cast operator is essential for what? Casting void pointers?
> >

> > No, it's essential for things like converting a pointer to one structure
> > type to a pointer to another type -- which is occasionally a reasonable
> > thing to do.
>
> Exactly.
>
> > It's not essential for casting to or from a void pointer.
>

> Well if you're willing to ignore compiler warnings for not
> casting void pointers, then why waste time typing the cast between
> structures? Just ignore those warnings too.

None of the compilers I use generate warnings for an assignment of a
void* pointer. But even if they do (a compiler can issue all the
warnings it wants), it cannot refuse to translate the code.

> As for other dangerous assignments, would you really trust
> something like,
>
> long int *lip;
> int *ip;
> :
> ip = lip;
>

> without a cast? What if you index them later? You might pass


> ip to another function and forget about it.. time passes, programmers
> forget little details in other functions, change something somewhere
> else, go boom.

Oops, that doesn't compile on my compiler. That code probably won't
compile on many compilers since it violates a constraint. Now adding a
cast here says, "I know what I'm doing," but you probably don't since
different pointer types are incompatible and the result of the
conversion is undefined.

> > > (Okay, sometimes you cast an int to a float to force a float division
> > > instead of an int division; but I like ending the post on that question..)
> >

> > Oh, so you're resorting to cheap rhetorical tricks instead of actually
> > having a sensible argument?
>
> I don't expect to win, I've seen lots of code left uncast.. and
> I will continue to see code left uncast, its not a major issue for me. :)
> I don't even like void pointers.

It seems you don't know how to use them correctly, so that may be why.

By the way, you can't honestly say that this isn't an important issue
for you - you've already written 20 or so 5+ page articles on the
subject.

> As for the rhetorical trick, I just thought it climactic. But,
> in the interest of fairness, I included the main use of a nonpointer cast
> in at the bottom (not that it really pertains to the "debate").

But it does.

William Hughes

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Derek Harmon wrote:

> ... would you really trust


> something like,
>
> long int *lip;
> int *ip;
> :
> ip = lip;
>
> without a cast?

No I wouldn't. I wouldn't trust it with
a cast either. If I write something like this
without a cast
my compiler will give me a warning.
It is a warning I would heed.
If I needed to do this I would use a cast,
but I would think long and hard for another
way before I would do this. If in the end
I had to do this I would add a comment
starting:

/**************** KLUDGE ALERT ******************

Using the very dodgy logic that if someone else says
it maybe you'll get the idea I will point out:

If the compiler accepts an assignment without
a cast the assignment is at least capable of doing
something sensible. It is guaranteed that the next
version of the compiler, or a compiler on another machine
will also do something sensible, and in general,
something with the same result (as far
as program output goes).

If you use a cast, the compiler will do something, whether
it is sensible or not. Therefore, if you use a cast it is up to
you alone (without help from the compiler)
to be sure something sensible will happen.
Even if something sensible does happen, it is far
from certain that something sensible will happen on
another machine, or with the next version of the
compiler.

If you do not use unnecessary casts for documentation,
the very few pointer casts you end up using will
be big warning flags that something dangerous
is going on here.

If you use unnecessary casts for documentation, you
lose this feature. You also lose the
feature by which the compiler warns you
about assignments that might not do something
sensible. Many of us think you are giving up
a lot and gaining very little.

-William Hughes


Chris Torek

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
In article <35B94F03...@ix.netcom.com> Derek Harmon

<de...@ix.netcom.com> writes:
> C has no programming by contract,

Ah, but it does: if you buy a conforming hosted implementation and
write strictly conforming programs, you *do* have a contract --
maybe even a legally-enforceable contract, although anyone with
sufficient legal experience knows that "legally enforceable" is an
adjective whose proof is in the enforcing. Specifically, the C
Standard acts as a contract, circumscribing the effect of each
strictly conforming construct.

(Of course, as many people will point out, most C programs are not
strictly conforming. People often use implementation-defined
behavior, or otherwise-undefined behavior that has been defined by
a particular implementation. These are not quite the same thing,
at least if one is speaking in standard-ese. Either way, however,
a programmer who sticks to "defined behavior", however it has been
defined, can be sure what the code will do.)

>... if you're willing to ignore compiler warnings for not
>casting void pointers,

This is just the point: C compilers do not -- or should not -- emit
warnings when assigning between "void *" and any other pointer type.

Any C compiler is, of course, free to emit spurious warnings at
will, but there is no call for warnings in this case. Indeed, the
type "void *" exists only *because* the ANSI C committee members
wanted some way to make it possible to assign "generic" pointers,
such as those returned by malloc(), to specific pointers, and vice
versa, without casts. That is:

#include <stdlib.h>
...
some_type *p = malloc(sizeof *p);

is entirely legal and should *not* draw a diagnostic. (This is
*not* true for C++, but this is comp.lang.c, not comp.lang.c++.)

> As for other dangerous assignments, would you really trust


>something like,
>
> long int *lip;
> int *ip;
> :
> ip = lip;
>
>without a cast?

Would you trust it with one?

Once again: a cast is a conversion, and semantically the same as
an assignment. The Standard says, in part (§6.3.4, Cast Operators):

Conversions that involve pointers, other than where
permitted by the constraints of 6.3.16.1, shall be
specified by means of an explicit cast.

This "shall" is the reason "ip = lip" must draw a diagnostic --
it does not meet the constraints in §6.3.16.1 (Simple Assignment).
However, 6.3.16.1 *does* include these words:

One of the following shall hold: ...
- one operand is a pointer to an object or incomplete
type and the other is a pointer to a qualified or
unqualified version of void, and the type pointed to by
the left has all the qualifiers of the type pointed to
by the right; or ...

and this is the reason "p = malloc(sizeof *p)" should *not* draw
a diagnostic.

Since, other than the suppression of diagnostics, the effect of a
cast is the same as that of assignment to a temporary variable
whose type is given by the cast, the *only* thing the cast in:

ip = (int *)lip;

does is -- what else? -- suppress the diagnostic. (Of course,
suppressing the diagnostic is a good thing whenever this code will
do something valid and useful ... although when *that* might be,
I could not say. Moreover, given similar code that "happens to
work" on some set of machines, I have found that the code can be
transformed -- and often simplified -- into something that is more
conformant, using "void *" and/or "char *" to hold intermediate
pointers. [Here "more conformant" means, loosely, "makes less use
of behavior specific to certain implementations".])
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA Domain: to...@bsdi.com +1 510 234 3167
Antispam notice: unsolicited commercial email will be handled at my
consulting rate; pyramid-scheme mail will be forwarded to the FTC.

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
John Kugelman wrote:
> You're proving tiring. Show me of an example where a void* pointer is
> used that has an identifiable type but cannot be replaced by that type.
> You are making me believe that you don't really understand the purpose
> of void* pointers, which is to hold generic, typeless data. void* HAS
> NO TYPE. If your pointer, don't use void*.

Just let me make my point clear, eventually the pointer _will_
_have_ a type. It will receive this from a void* or some other * not
of the same type. Then it must be type cast. You have the option of
not type casting, and may or may not (char*s and things the compiler
may see as compatible) get a warning. Whether it produces a warning
or not (it _usually_ will), it should be cast. Here's a "Re-Hash"...

void *hash_table[101];

int hash(const char *name) {
if (name) {
return name[0] % 101; /* pathetic hash fn, I know */
} else return -1;
}

void *hash_lookup(int key) {
return ((key < 0) || (key > 100)) ? NULL : hash_table[key];
}

void *hash_lookup_by_name(const char *name) {
return hash_lookup( hash(name));
}

This sort of example has been floating around the thread..

So, suppose I put apples and oranges into the hash ADT.. now I
want to get them out by the name of the fruit ("Tangerine Orange",
"Macintosh Apple", "Big Red"). When I get them out I want to display
a message box depending on the type, and I should be able to determine
their type when I retrieve their void pointer. In assigning that
pointer anywhere in my functions to display fruit, I want to cast it
to (struct apple *) or (struct orange *).

Someday I may add more fruit. Suppose I add tomatoes, then I
end up getting a hash table collision for "Big Red" tomatoes. If I
add tomatoes to the display fruit function, I want to cast it as
(struct tomato *).

Now eventually I'm going to debug this after Big Red comes up
displaying an tomato message box when I expected an apple message box,
since the tomato struct overwrote the apple struct of the same name,
effectively, in the hash table. This will be easier if there are
typecasts on the pointer that was void when it as sonn as possible
after its return from the hash function. If you make the assignment
from a void pointer to a typed pointer, you must know at that point
what the types are.

> If you can't find that typo within ten seconds you don't deserve to be
> programming in C.

You must have good eyes, or a large 27" monitor, or an 18 point
font, or something.. :) On my monitor, an "a" and an "s" look very
much alike.

Besides, if a person cuts and pastes large tracts of code with
minor alterations required between replications (a practice that has
been brought up as a reason not to cast because it requires more work),
and you're in a hurry (as I said, a reason not to cast because it
requires more work, which implies you are in a hurry).. then leaving
out one alteration in thirty replications is quite possible (as is,
in fact, leaving out the same alteration on all thirty replications).

This is why cutting and pasting code can be dangerous.. such
typos frequently go to compilation to be detected.

> "Protecting against typos" is a stupid reason to add
> a cast. You should add a cast if you need its semantics in your code
> (which may be never in strictly conforming code), not to protect
> yourself.

Every little bit of syntax protection is helpful. The cast makes
the compiler check that the rhs and lhs are the same type.. they should be.

Protecting themselves is the major reason I hear for not casting
malloc, protect me from forgetting to include stdlib.h. Then they go to
C++ with that solution and have extra work with it, or they cut & paste
plenty of mallocs and they make a typo or forget an alteration.

> > This nefarious malloc() typo is also well known to strike in
> > replicated code! You do some malloc's in one function and want to cut

:


> Shouldn't you been using templatized cut and paste or a Perl script for
> this?

Templatized cut and paste: yes, now; didn't have that years ago
and I remember something similar to that happening on occassion. Besides,
people keep raising replicated code as an argument against casts, its an
argument for them as well. It's really a wash.. and my opinion is repli-
cated code is just as dangerous as the pointer assignments that must be
cast, so I try and avoid them.

> Coding speed has *no* relation to typing speed. If you find yourself
> typing 90 WAM you need to slow down and think some more.

No, I'm only 30 WAM.. still no reason to slow down. :) If I
leave off a semicolon within a new function, the compiler will tell me,
I'll fix it, I'll recompile and be done. If I'm going 6 WAM.. I'm
still only a fraction of the way through the function.

> > If it's a void pointer the compiler can only say its suspicious.
>
> It's only suspicious if you screwed up. Don't do that.

Bingo. :D Now, the issue dwindles down to assignments out
of void* that you don't consider suspicious. I consider them all
suspicious, excepting: (i) exchanges between void* s (which don't
count because they are the same type and I've already pointed out
casts between the same type are inane and wasn't intended part of
my doomall statement), (ii) receiving the return of malloc()
(which has been well discussed, and you won't convince me the
cast isn't worth guaranteeing with the programmer's official seal
the type out of malloc(); or any other void pointer function).

> That's not what a cast operator is for! Get that through your head. A
> cast is used to change the type of an object or value to another, not to

This is a good definition of cast, go with that. If you're
reading source code the use of a cast should indicate something to you.

The same as,

int func(int a, int b);

Tells you a poorly named function returns an int, and takes two
poorly named integer arguments. You don't need a comment for that.

A well-written C program largely documents itself. Perhaps you've
written so many well-written C programs that you no longer notice that. :)

Derek Harmon

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
John Kugelman wrote:
> > Those aren't words, that's the electron beam gun scanning across the
> > rows of phosphor on the inside of the monitor. :D
>
> Could you at least try to stay relevant?

Sorry.. :/ :)

> > Casts don't tell the truth or lies, the humans that use them do.
>
> I have no idea what that means.

In other parts of the thread people have said that when a programmer
uses a cast, the programmer is lying to the compiler. My premise is that
the programmer will only tell the compiler a cast for an honest reason to
make a cast which will not violate constraints (and thereby quiet the
compiler - or as has been put, "Compiler, Shut Up!") Not that the pro-
grammer will cast when the cast shouldn't be done (is dangerous) and is
deceptive in nature (and in these cases, my assertion is the _assignment_
itself shouldn't be done).

> > As for other dangerous assignments, would you really trust
> > something like,
> >
> > long int *lip;
> > int *ip;
> > :
> > ip = lip;
> >

> > without a cast? What if you index them later? You might pass

:


> Oops, that doesn't compile on my compiler. That code probably won't
> compile on many compilers since it violates a constraint. Now adding a

That's the point.

> cast here says, "I know what I'm doing," but you probably don't since
> different pointer types are incompatible and the result of the
> conversion is undefined.

I hadn't considered that the result of a typecast might be
undefined, good point. :) Of course, the assignment is a dangerous
one nonetheless. Nevertheless, I've seen pointers typecast in which
the conversion went along seamlessly although I now acknowledge that
might vary from implementation to implementation.

The point is that a cast needs to be made, if the programmer
wants to do that, which I never said was a good idea.

I hope you aren't still operating under the guise that I'm
advocating,

int *ip1;
int *ip2;

ip1 = (int *)ip2;

because I disavowed that _long_ ago in this thread. We're
talking about pointers to different types. Whatever exceptions there
may be to such assignments, I advocate they all be typecast if for
no other reason than to document the requirement in-code that both
sides are intended to be that type. I assume a programmer who makes
a cast is thinking this, otherwise the assignment should be made in
the first place.

If you show me some examples of pointer assignments between
pointers of different types that should _not_ be typecast, I'd much
rather attack that than continuously be on the defensive. ;)

> > > Oh, so you're resorting to cheap rhetorical tricks instead of actually

: :

> > I don't even like void pointers.
>
> It seems you don't know how to use them correctly, so that may be why.

The cheap rhetorical tricks or the void pointers? :D Oh, the
void pointers, right. Well, you have the right to your own coding style,
but I tend to think the same thing if you assign from void pointers
without casting.

> By the way, you can't honestly say that this isn't an important issue
> for you - you've already written 20 or so 5+ page articles on the
> subject.

Sun pushed my buttons, what can I say? :)

> > in at the bottom (not that it really pertains to the "debate").
>
> But it does.

int hits, atBats;
float battingAverage;
:
battingAverage = (float)hits / atBats;

Doesn't look to involve a pointer cast anywhere?

Derek Harmon

Aaron Crane

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
In article <35BA2AF1...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> The cast makes the compiler check that the rhs and lhs are the same type.

FCOL, no it does not.

It does just the opposite -- it *prevents* the compiler from checking that
the rhs and the lhs are of the same type (unless you're casting the rhs to a
type incompatible with the lhs, which would make yet another bug in your
code).

I'm ignoring the rest of the crud in your article so that the essential fact
of how wrong you are about what the language specifies will be readily
apparent.

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Aaron Crane wrote:
> It is designed for lying to the compiler. If you use it for something else
> (`documentation') then your understanding of C has a fundamental error.

It is designed to tell the compiler something (and presumably, the
programmer reading the C source code). Everything in C inherits this
common attribute. Now, if you tell the compiler a _lie_, then that's
up to you.

There's nothing wrong with telling the compiler (and other pro-
grammers who may read the code) to cast the type when its the _truth_,
and presumably the compiler wouldn't otherwise know (the types are
not the _same_). But the issue has been raised that a typecast between
different pointer types is undefined.. that might be wrong if you're
moving between implementations.

> Occasionally, the programmer knows more about what is going on than the
> compiler; in these circumstances, a cast is appropriate, and it is required

Hopefully its use is only done "occasionally," I dread debugging
programs that were written which use void pointers like a plague, everywhere
without a cast. I've stated from the get-go that using a cast is a programmer
saying, "I know what I'm doing."

> > It can be used as a form of documentation,
>
> But only if you don't know what it's really used for. See above.

No, if you don't know what a cast is really used for, then a cast
is worthless. But when used correctly in appropriate situations, it is a
form of documentation. Whether or not a later programmer pays any atten-
tion to it, recognizing the information it conveys, is another matter.

> Much of your argument for explicit casts on (void *) values has been based
> on the `idiot programmer' concept, yet now you assume that programmers apply

I've said I've seen source code written by idiot programmers that
I had to maintain that should've been cast. Other respondents have said
the rampant uses of void pointers I've seen which I advocate casting as
what should've been a way to prevent the spread of their disease sooner,
were examples of idiot programmers. I agree.

I avoid those dangerous coding practices, first with thorough
analysis, then thorough design, and by the time I get to coding there's
rarely a time when a void* is necessary (see an earlier post in the
thread for the times a void* is necessary).

> casts knowledgeably? I find that, on the contrary, the vast majority of
> explicit casts I have seen in production code represent misconceptions or
> poor design on behalf of the programmer.

Usually its the underlying assignment that represents the
misconceptions and poor design. If a programmer had tried to put an
intelligent cast there either he would've shown himself the assign-
ment reveals misconceptions and poor design, or he would've shown
himself to be an idiot. :)

> On the contrary, you are guaranteed that if you perform an uncast assignment
> between incompatible types, you will get a diagnostic; at this point, the
> implementation is free to stop translating.

If he is actually making the assignment, then it already shows
he's going to ignore the diagnostics of the compiler. The implementation
is also free to continue translating, and if there's a question to that
the programmer probably turned off that vital compiler functionality.

I suppose you're saying by leaving it uncast, the compiler will
then tell you that it's dangerous. Your presumption has been that the
programmer knows the types and they should be close by, if he sees that
and knows that they're different, why would he care about the compiler
telling him so?

If he knows them, he should cast them. If he doesn't know them,
he should go look them up now, come back, and cast them. In this process
he will hopefully figure out whether its a good idea to perform the
assignment or not, before applying the cast. If he has concluded it is
a good idea, then he may as well shut up the compiler (if he compiles
til the code is warning free, for example).

> I assume that this statement is an analogy to the argument that `guns don't
> kill people, people do'. You are saying that casts are not in themselves
> bad, just as guns and bullets are not in themselves bad. But just as you
> can't shoot someone without a weapon, you can't lie about your data without

You also can't tell the truth about your data without a cast or
some related construct.

> a cast or some related construct. So why are you advocating casts in places
> where the language doesn't require them?

In most cases the language does require them, I think we've
really gotten too far away from the real issue of making explicit,
casts on void pointers:

[Quoting from Simon's original post.. the one that started it all.]
# H&S states that explicit pointer casts are generally a good idea, but
# ( according to the FAQ ) an explicit cast of the pointer returned from
# "malloc" is a bad idea. Are explicit casts a good idea? does this
[^strong implication of _pointer_
casts, cf subject and H&S citation.]
# also apply to casts to/from the void * type? Should we differentiate
# between casts to void * and casts of other pointer types?
:
# My opinion is that casts to/from void * should be
# a matter of style...

My opinion is that casts to/from void * should be made explicit.
(The suggestion begs the implication that casts from void * are presently
considered optional, and these generate compiler diagnostics that are
being ignored.)

Why would I suggest casting _to_ void * (we'll start with some-
thing admittedly unpopular), as in,

void *vp;
int *ip;
:
vp = (void *)ip;

Where this cast is not strictly required (implicit).. this is
relatively less significant as a void * is used to hold a typeless
address. However, for who, for what? Outside the three plausible
exceptions which justify void pointers already cited, this void pointer
will come out someplace else. At that point, it is most convenient to
find "void pointer creation points" by scanning for "(void *)" type
casts. There's no real convenient comment for this, certainly nothing
standard, other than the void typecast marker itself.

But perhaps programmers don't want this.. it's certainly frightening
if they already have LOTS of void pointers all over the place! But then, that
should tell them something else.

If you assign something into a void pointer variable, then it
likely (not always) will be assigned out of a void pointer variable
someplace else.

Those assignments out of the void should be typecast. Now,
the question seems to imply that some are and some aren't, which sounds
familiar to me as a shop that has this particular diagnostic turned off..
they should be typecast. If they can't be typecast, then the assignment
shouldn't be made in the first place.

Additionally, the typecast provides convenient in-flow information
that the programmer has analyzed this assignment and affixed his seal that
this is approved. Its useful to scan the source for places where a certain
type "(struct matrix_type *)" received its value from a void pointer (or a
compatible but non-matrix_type pointer). No readily standardizable comment
exists to easily track down this information, other than the typecast itself.

Whether to typecast the return of malloc or not was acknowledged as
an exception in my original reply (the very _second_ sentence, the contro-
versial one). Reasons ground into the pavement.

Whether you want to quietly accept the compatibility of char* and
void* because of backward compatibility for malloc() and not cast it, that
is your choice but I'd recommend casting it as anything else coming out of
a void*. Whether you want to quietly accept assignments of compatible structs
then you can choose to leave them uncast but I'd recommend casting to verify
that you know what you're doing. You'd be surprised how many programmers
don't.

If you post some of these exceptions to assigning _from_ a void
pointer (other than malloc(), I've already given all the reasons for that
and nobody's been convinced yet.. go ahead, keep your warning about for-
getting stdlib.h), I'll eagerly explain the rationale behind typecasting.
Will let me do a little more attacking. :)

> > Omitting the cast of a void pointer guarantees nothing, only that the
> > programmer is crazy.
>
> The programmer is crazy to allow the compiler to perform static type
> checking? Do you realise how stupid that statement is?

My meaning was the programmer is crazy for _performing_ the
assignment from a void pointer that he left uncast. If he can cast
it, he should. If he cannot cast it, he should delete the assignment
and re-study the problem. The programmer is already aware he's going
to get a diagnostic and that he'll disregard it (we're talking about
shops where casting to/from void is optional, not places where "It don't
leave the shop until it compiles diagnostic-free.")

Compiling diagnostic-free is a good thing, I do it (at least,
unless the system I'm supporting already compiles with over 600+ such
bad pointer diagnostics, and I'm told don't fix them until they break).
But the question of leaving assignments _from_ void* optionally uncast
already implies the later situation.

> (2) requires a diagnostic, as it violates the third constraint listed in
> 6.3.16.1. Since it requires a diagnostic, the compiler may stop translating
> at this point.

Read, the compiler may continue translating at this point..
I don't claim that's a good idea, don't get me wrong. Are you telling me
you've never seen production code saying something castless to the effect,

void *vp;
char *cp;
:
cp = vp;

???

> If you still can't see the distinction, why not give up on C, which is
> clearly too difficult for you?

Well, at least I see the distinction. It's certainly not lost on
me. We both just read the original question (and code in the real world)
differently.

> Yes. And without a cast, you get a diagnostic every time you translate that
> code, but with a cast, the compiler assumes you know best.

:: sigh :: :)

> > I don't expect to win,
>
> You can't win a technical discussion with no understanding of the issues
> involved, so this statement doesn't surprise me.

It might be surprising to everybody in this thread that I
actually don't have very many pointer typecasts in my code outside
malloc() (since I like to reuse what C solutions I can in C++, appreciate
typo insurance more than stdlib.h forgetfulness insurance, and possess a
conceptual affinity of the notion of clearly identifying type changes).

I grepped on " *)" on all 1998 .C source files written personally
for myself (amounting to 4,108 lines in total which includes blank lines
and comment lines, the ratio of which is about 1/3rd, doesn't include any
[majority] code done for work) and then subtracted the "alloc" count
(of which there were 74) and came up with _ELEVEN_ (11, about one quarter
of one percent of my lines containing a non-malloc pointer typecast).

Personally, I infrequently typecast (void *) when creating a void
pointer, but most of my personal code is only hundreds of lines. I can't
overstate its utility in large production code.

> Derek, you seem like a nice enough person, even if you are astoundingly
> misguided on this topic. If you give up now, I shouldn't think anyone will
> hold it against you.

You might be a nice enough person too, Aaron. :) But I think you
and others have misinterpreted the issues involved with the original post
(--or, perhaps I have misinterpreted, maybe the original poster rarely
uses void pointers and does himself typecast them regularly when
required--), you cannot answer that (well, you can, but with a certain
irrelevance as I see it) saying C requires typecasts from void* 90% of
the time because the implication is that the void* is already cast at
the option of the programmer (and his boss is just making it mandatory
now - which is only appropriate IMO).

Now the other 10%, why argue over, just cast them. Keep things
consistent, easy to detect, make them stand out. :)


Derek Harmon

William Hughes

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Derek Harmon wrote:

>
>
> I hadn't considered that the result of a typecast might be
> undefined, good point. :) Of course, the assignment is a dangerous
> one nonetheless. Nevertheless, I've seen pointers typecast in which
> the conversion went along seamlessly although I now acknowledge that
> might vary from implementation to implementation.
>
> The point is that a cast needs to be made, if the programmer
> wants to do that, which I never said was a good idea.
>

I am now completely lost as to what your point might be.

There are basically three categories of casts.

Casts between non-pointer types:

The most common example is the widely
used cast of an integer type to a floating point
type to force floating point arithmetic. The
cast here is absolutely necessary, and
there is no other easy way to achieve the same
result. There are times when you may want
to cast between different struct types,
but if you want to do this you will have to use
the cast.

Everyone seems to be agreed.

Casts between pointers not involving void pointers

If the pointers are of the same type no cast
is necessary or should be used. If the pointers
are of different types a cast is necessary and
should be used. The second case is a signal
that the code may not be portable or stable.
The second case should be uncommon in
good code (many programmers never need
to cast between two pointer types).
Everyone seems to be agreed.

Casts between pointers where one pointer is a void *

We have something like

int main(void)
{
int * m_pnt;
void * store;

then the two fragments

store = (void *) m_pnt;
m_pnt = (int *) store;

and

store = m_pnt;
m_pnt = store;

are equivalent. The compiler does exactly the
same thing whether you use the casts or not.

Arguments for using the casts:

It provided a form of self documenting
code. [many argue that the self documentation
is of little or no value]

Arguments against using the casts:

If you cast the return of a function returning
(void *) you can hide a nasty error. If the
prototype is not in scope, the compiler will
assume that the function returns int. It will
then try to cast the int to a pointer, and it
will not complain. If you don't use the cast
you will get a diagnostic. This is the usual
argument for not casting the return value of
malloc.

If you make an error such as declaring
float * store;
(e.g. you thought you would only be using
float pointers, or you modified code that
only used float pointers) and you try
m_ptr = store;
the compiler will issue a warning or an error.
If you try
m_ptr = (int *) store;
the compiler will not issue a diagnostic
and try its best to do the cast. The really
nasty thing is that this will work on some
machines and fail on others.

If you make a habit of casting pointer types
the cast of a pointer type will no longer indicate
a dangerous piece of code.

(yes you can shoot yourself in the foot by doing
something like

int * m_ptr;
float * f_ptr;
void * store;

store = m_ptr;


(lots of code maybe a function call or two)

f_ptr = store;

but the use of casts won't help you )


Given these arguments, a lot of people decide that
casting pointer assignments involving void pointers
is a bad idea.

So where do you disagree?


-William Hughes


Aaron Crane

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
In article <35BA513F...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> Aaron Crane wrote:
> > On the contrary, you are guaranteed that if you perform an uncast
> > assignment between incompatible types, you will get a diagnostic; at
> > this point, the implementation is free to stop translating.
>
> If he is actually making the assignment, then it already shows he's going
> to ignore the diagnostics of the compiler. The implementation is also
> free to continue translating, and if there's a question to that the
> programmer probably turned off that vital compiler functionality.

Again, you show your ignorance of C. There is a fundamental difference
between warnings that an implementation chooses to emit and the required
diagnostics that the standard describes.

> I suppose you're saying by leaving it uncast, the compiler will then tell
> you that it's dangerous.

The compiler should tell you not that it's dangerous, but that it's an
error. If your compiler tells you something else, then it translates a
language similar to C but not in actual fact the standard C language.

> > I assume that this statement is an analogy to the argument that `guns
> > don't kill people, people do'. You are saying that casts are not in
> > themselves bad, just as guns and bullets are not in themselves bad. But
> > just as you can't shoot someone without a weapon, you can't lie about
> > your data without
>
> You also can't tell the truth about your data without a cast or some
> related construct.

I beg your pardon? I'd like to know language you are talking about here;
it certainly isn't standard C, which expects you to always tell the truth
about your data.

For completeness, the `related constructs' I was talking about are:

* Assigning a value to one member of a union and extracting another
member.

* Defeating type checking on pointer casts by assignment through a
pointer to void:

int *ip;
char *cp;
void *vp;

/* snip */
vp = cp; /* legitimate */
ip = vp; /* legitimate on its own but very stupid here */

> > a cast or some related construct. So why are you advocating casts in
> > places where the language doesn't require them?
>
> In most cases the language does require them, I think we've really gotten
> too far away from the real issue of making explicit, casts on void
> pointers:

In this case the language EXPLICITLY DOES NOT REQUIRE A CAST. I'm sorry for
shouting, but I've said this several times now, and you still haven't got
the message. Once more: CASTING TO OR FROM A POINTER TO VOID REQUIRES NO
CAST IN C.

If you don't believe me, read clauses 6.3.4 and 6.3.16.1 of the standard.
Even better, go and take an introductory C course.

> My opinion is that casts to/from void * should be made explicit.

Fine. You're welcome to your misguided opinions, but please note that your
technical arguments in favour of this opinion are, quite frankly, the sort
of lunacy I'd expect from Herbert Schildt. Moreover, you persist in telling
some of the world's foremost C experts that these arguments are correct.

> (The suggestion begs the implication that casts from void * are presently
> considered optional,

They are. 6.3.4, 6.3.16.1.

> and these generate compiler diagnostics that are being ignored.)

No, they generate compiler warnings which are distinct from required
diagnostics.

> At that point, it is most convenient to find "void pointer creation
> points" by scanning for "(void *)" type casts.

Or you could look for variables declared as pointer to void. What's your
point? That you should abandon type checking, littering your code with
nonsense, just in case at some point in the future you should want to find
all such instances.

> There's no real convenient comment for this, certainly nothing standard,

That's right. If you think this is crucial, develop a coding standard which
requires a comment of a certain form, and persuade your development group
that it's a good idea.

> But perhaps programmers don't want this.. it's certainly frightening if
> they already have LOTS of void pointers all over the place!

Yes, I use pointers to void all the time. Every time I allocate memory, I
use a pointer to void. Every time I store a user-defined type in a generic
container ADT, I use a pointer to void (and I make that container contain
*only* objects of that type). Every time I write generic container ADTs, I
make them accept pointers to void.

> But then, that should tell them something else.

What's that then?

> Those assignments out of the void should be typecast. Now, the question
> seems to imply that some are and some aren't, which sounds familiar to me
> as a shop that has this particular diagnostic turned off.. they should be
> typecast.

This is *really* the central part of your argument: your opinion that such
casts are good. I'm happy that you have an opinion. Now please take it
away, because quite frankly it stinks nearly as badly as the idiocies with
which you attempt to support it.

> Whether you want to quietly accept the compatibility of char* and void*

And the compatibility of (void *) and a pointer to any object or incomplete
type -- a fact which you have consistently refused to accept.

> Whether you want to quietly accept assignments of compatible structs

In C, compatible structs are those whose members have exactly the same names
and types and appear in the same order. Again, you are fundamentally
confused about C's type system.

> then you can choose to leave them uncast but I'd recommend casting to
> verify that you know what you're doing. You'd be surprised how many
> programmers don't.

Like you, for instance. And casts in your code will by no means assure me


that you know what you're doing.

> > > Omitting the cast of a void pointer guarantees nothing, only that the


> > > programmer is crazy.
> >
> > The programmer is crazy to allow the compiler to perform static type
> > checking? Do you realise how stupid that statement is?
>
> My meaning was the programmer is crazy for _performing_ the assignment
> from a void pointer that he left uncast. If he can cast it, he should.

But casting it prevents type checking, and is therefore crazy.

Imputing craziness for not liking *your* bizarre coding style is not a good
way to approach a technical discussion.

> I don't claim that's a good idea, don't get me wrong. Are you telling me
> you've never seen production code saying something castless to the effect,
>
> void *vp;
> char *cp;
> :
> cp = vp;

What's your point? This is perfectly correct code, and I (and a lot of
other people) also consider it good style. If I saw that code with a cast
on the rhs of the assignment, I'd assume that, like you, the programmer
didn't understand C's type system.

> I like to reuse what C solutions I can in C++,

This explains so much. You are attempting to write in some peculiar hybrid
of C and C++, in which a pointer to void is compatible with nothing else,
and in which pointers to classes with a certain inheritance relation are
compatible. So please take this crud out of a newsgroup which discusses
standard C.

I find it tiresome in the extreme to attempt to teach C to someone who
believes mistakenly that he knows what he's talking about, and persists in
demonstrating his ignorance to the world at large while calling it fact.
Please go away.

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Aaron Crane wrote:
> Derek Harmon <de...@ix.netcom.com> writes:
> > The cast makes the compiler check that the rhs and lhs are the same type.
>
> FCOL, no it does not.

struct apple *a;
struct orange *o;
:
a = malloc( sizeof *a);
a = malloc( sizeof *o); /* this line cut & paste and not fully */
: /* fixed. */
:
a->field_of_apple ... /* Error */

I have to run that to find that out.. now, if I had used a cast as follows,

a = (struct apple *)malloc(sizeof *a);
a = (struct orange *)malloc(sizeof *o);

Then the compiler would check the second line and see that the lhs (type
struct apple pointer) isn't the same as the rhs (type struct orange
pointer) and it would issue a diagnostic.

> It does just the opposite -- it *prevents* the compiler from checking that
> the rhs and the lhs are of the same type (unless you're casting the rhs to a
> type incompatible with the lhs, which would make yet another bug in your
> code).

Oh, okay, guess you saw that one coming. :) It's a plausible mistake that
could come up in the course of coding. The alternative that you want the
compiler to check for can be eliminated in any good design. If the
programmer puts a cast there, the programmer is already acknowledging
that the compiler would've spoken up about that line.

> I'm ignoring the rest of the crud in your article so that the essential fact
> of how wrong you are about what the language specifies will be readily
> apparent.

LOL. ;D

You're still arguing this from the language specification point of view,
I'm explaining it from the programmer-dependent point of view. I haven't
claimed that the language specifies you must cast all pointers. So, it
should surprise no one we're at a loggerheads.

Derek Harmon

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
William Hughes wrote:
> There are basically three categories of casts.
>
> Casts between non-pointer types:
:

> Everyone seems to be agreed.

Yes. :)



> Casts between pointers not involving void pointers

:


> Everyone seems to be agreed.

Yes. :)



> Casts between pointers where one pointer is a void *
>
> We have something like
>
> int main(void)
> {
> int * m_pnt;
> void * store;
>
> then the two fragments
>
> store = (void *) m_pnt;
> m_pnt = (int *) store;
>
> and
>
> store = m_pnt;
> m_pnt = store;
>
> are equivalent. The compiler does exactly the
> same thing whether you use the casts or not.

Here I would definately perform m_pnt = (int *)store; and I
would strongly encourage one to use store = (void *)m_pnt;

I've outlaid the benefits to readability of such casts, I don't
contest the compiler doing the exact same thing whether or not the casts
are used (they are useful as syntax).

> Arguments for using the casts:
>
> It provided a form of self documenting
> code. [many argue that the self documentation
> is of little or no value]

If you cast the return of any function returning void * then your
code is more easily portable to C++.

A cast on a function returning void * to a typed pointer instructs
the compiler to issue a diagnostic if the typed pointer on the lhs and the
typecast on the rhs (representing the expected type of the rhs as determined
with great forethought by the programmer) are not in agreement, providing a
double locking mechanism (this has both a positive and negative impact on
the ease of changing that line of code).


> Arguments against using the casts:
>
> If you cast the return of a function returning
> (void *) you can hide a nasty error. If the
> prototype is not in scope, the compiler will
> assume that the function returns int. It will
> then try to cast the int to a pointer, and it
> will not complain. If you don't use the cast
> you will get a diagnostic. This is the usual
> argument for not casting the return value of
> malloc.
>
> If you make an error such as declaring
> float * store;
> (e.g. you thought you would only be using
> float pointers, or you modified code that
> only used float pointers) and you try
> m_ptr = store;

^
m_pnt { int pointer }

> the compiler will issue a warning or an error.
> If you try
> m_ptr = (int *) store;

^
m_pnt

> the compiler will not issue a diagnostic
> and try its best to do the cast. The really
> nasty thing is that this will work on some
> machines and fail on others.

Point of the strawman recognized. I ammend that it protects from
many type conflicts, not all. Of course, applying a cast to allow an int
pointer to a float pointer is itself suspect in the first place, so if the
programmer "tries" this he probably wasn't thinking straight. When
debugging, a programmer should see that store is used on this line as an
int *, and is declared as a float *, and will change one thing or the other
to fix the problem. This demonstrates the degree of information contained
just in the type cast and the variable types themselves.

So that is to say, in this example, the compiler would detect the
bug faster for the programmer who had a brief attack of attention deficit
disorder; however consistent use of casts between mixed pointer types
across the code of a large piece of software will make debugging the
majority of errors easier.



> If you make a habit of casting pointer types
> the cast of a pointer type will no longer indicate
> a dangerous piece of code.

If you make a habit of performing the underlying programming
which necessitates casting pointer types... you may experience typecast
insensitivity.



> (yes you can shoot yourself in the foot by doing
> something like
>
> int * m_ptr;
> float * f_ptr;
> void * store;
>
> store = m_ptr;
>
> (lots of code maybe a function call or two)
>
> f_ptr = store;
>
> but the use of casts won't help you )

Let's re-write that with type casts and see, shall we? ;)

int *m_ptr;
float *f_ptr;
void *store;

store = (void *)m_ptr;
:
(lots of code, maybe a function call or two later)
:
f_ptr = (float *)store;

You identify a bad floating point number somewhere and trace
it to f_ptr. Oh, there's a typecast here, store must not be a float
pointer. Let's see where store got it's value,

If your coding style involves one space to either side of the
assignment operator, then you just search on "store = (void *)" and
you jump back instantly. You see it getting a value from m_ptr, you
lookup the declaration of m_ptr, oh, it's an int *. Here is the error.

Of course, the process of putting a (float *) on store in the
first place should definately cause the programmer to double-check
what store is. I'm not advocating blind type casting just because
two types are different, only casting when you know what you are
doing. If he didn't know, then he should've stopped coding right
there and re-examined matters.

Now, without casts, these are just plain-looking assignments.



> Given these arguments, a lot of people decide that
> casting pointer assignments involving void pointers
> is a bad idea.

I agree with this statement. Frankly, this revelation took me
completely off-balance, I thought there'd be some strong typists out
there. And the total disregard for typecasts as a prime means of
documentation was also totally surprising. Few things in C are so
informative when they appear, and so consistent in their appearance.

Any shaky type cast for certain deserves a comment somewhere near,
but searching on comment text is a much less consistent tool to rely on
when you are charged with maintaining 100,000 lines of source code.

> So where do you disagree?

First, I appreciate the time you've taken William, to be an
'impartial' arbitor in assembling all of this in one place.
Thank you. :)

I've accentuated the above points which I feel are a little
stronger than presented. I was very shocked that more people don't
cast; although from the code I've seen on the job that's not so very
shocking (just easier to break and harder to debug). I think casts
are underrated, properly used they convey more information than is
popularly thought, and used prudently they impart greater analysis
of the statement they are attached to. If they didn't belong there
(the code was threatening), then it is my position the programmer
would delete the threatening code, not apply a false typecast.

Derek Harmon

Derek Harmon

unread,
Jul 25, 1998, 3:00:00 AM7/25/98
to
Aaron Crane wrote:
> Derek Harmon <de...@ix.netcom.com> writes:
> > Aaron Crane wrote:
> > > this point, the implementation is free to stop translating.
> >
> > The implementation is also
> > free to continue translating
>
> Again, you show your ignorance of C. There is a fundamental difference
> between warnings that an implementation chooses to emit and the required
> diagnostics that the standard describes.

Appears we see this two different ways. I didn't _disagree_ with
you, I pointed out the _other_half_ of your statement.

"The implementation is free to stop translating.."

means the exact same thing as,

"The implementation is also free to continue translating.."

Now, if the compiler were working a hard ten hours that day and
wanted to go home to it's family, perhaps the compiler would stop its
translation right there. But the compiler is a computer program, which
is tantamount to slavery, it probably has a switch that says compile
object code for anything short of an error- or fatal- diagnostic (I'm
not saying you will find this switch in the ANSI Standard anywhere...
I'm just saying you will find this on real-world implementations).

> > I suppose you're saying by leaving it uncast, the compiler will then tell
> > you that it's dangerous.
>
> The compiler should tell you not that it's dangerous, but that it's an
> error. If your compiler tells you something else, then it translates a

If it's an error, than why isn't the "implementation _required_
to stop translating?"

> > > I assume that this statement is an analogy to the argument that `guns
> > > don't kill people, people do'. You are saying that casts are not in
> > > themselves bad, just as guns and bullets are not in themselves bad. But
> > > just as you can't shoot someone without a weapon, you can't lie about
> > > your data without
> >
> > You also can't tell the truth about your data without a cast or some
> > related construct.
>
> I beg your pardon? I'd like to know language you are talking about here;
> it certainly isn't standard C, which expects you to always tell the truth
> about your data.

If "standard C .. expects you to always tell the truth about
your data," and "you can't lie about your data without typecasts" then
the conclusion of that sylogism(sp?) is, I think, that 'typecasts are
not standard C'.

Typecasts obviously pertain to standard C. They do have their
uses to tell the compiler the truth about your data when you, the
programmer, know more than the compiler can.

What if,

#include <stdio.h>

struct apple_like {
int type;
};

struct macintosh_apple {
int type;
int something_mac;
};

struct windows_apple {
int type;
int something_win;
};

void *returnsPointerToAppleLikeStruct(int a);
void *vp;
int type;
:
vp = returnsPointerToAppleLikeStruct( 1);
type = (struct apple_like *)vp->type;
:
if (type == 1)
printf("%d", (struct macintosh_apple *)vp->something_mac);
else if (type == 2)
printf("%d", (struct windows_apple *)vp->something_win);

In this example the programmer knows more about vp then the
compiler does, so the program employs casts on the void pointer to
tell the compiler _the_ _truth_ about what vp points to.

I guess I just assume the programmer isn't going to lie, there's
nothing to be gained by telling the compiler a falsehood like..

(struct amiga_apple *)vp->something_cbm

when the program knows vp doesn't point to such a struct. Why
would the programmer want to do that? It's crazy.

> the message. Once more: CASTING TO OR FROM A POINTER TO VOID REQUIRES NO
> CAST IN C.

int func(void *ptr) {
return *(int *)ptr; /* Cast required (ptr could be char* or int*) */
}

void *p;
char c[2];
int i;
c[0] = '\0';
c[1] = ' ';
i = 64;
printf("%d|%d", func(p = c), func(p = &i));

(Note, the reason the cast is required isn't specifically because
the pointer could be a char* or an int* outside the function.. that was
just something I scribbled when I tried to get a void pointer to be two
things at the same time; and couldn't do so without the cast.)

> If you don't believe me, read clauses 6.3.4 and 6.3.16.1 of the standard.

Whether a cast is required or not depends on how what you do
with the void pointer.

> > My opinion is that casts to/from void * should be made explicit.
>
> Fine. You're welcome to your misguided opinions, but please note that your
> technical arguments in favour of this opinion are, quite frankly, the sort
> of lunacy I'd expect from Herbert Schildt. Moreover, you persist in telling
> some of the world's foremost C experts that these arguments are correct.

LOL. :) (on the Schildt comparison.)

I am surprised that the world's foremost C experts cannot read
the information a simple type cast offers, and that they only see the
type cast as a tool to lie to the compiler.

> No, they generate compiler warnings which are distinct from required
> diagnostics.

Please distinguish, I thought there were three sorts of diagnostics:
warnings, errors and fatal errors?

Besides, I thought we were all for compiling warning-free? To
compile warning free does require casts.

> > At that point, it is most convenient to find "void pointer creation
> > points" by scanning for "(void *)" type casts.
>
> Or you could look for variables declared as pointer to void. What's your
> point? That you should abandon type checking, littering your code with
> nonsense, just in case at some point in the future you should want to find
> all such instances.

Typecasts do not imply abandoning type checking. They allow the
programmer to impose his own type check over the generic typechecking the
compiler would've performed if the programmer said nothing. This is a
conscious decision by a presumably competent programmer to do so, if the
programmer arrives at the understanding that the underlying code he was
about to apply a type cast to is too unpredictable then the programmer
should delete that underlying code. Applying a typecast in the wrong
place is irrelevant, it presupposes the programmer wrote the wrong
place in the first place.

If you have to litter your code with typecasts, then your code isn't
well written in the first place. It should tell you something.

Typecasts are not nonsense to programmers who know how to read them.

In a big program it's not if you want to find instances where you
assigned to a void pointer, it's when (if the big program uses void pointers).

> > There's no real convenient comment for this, certainly nothing standard,
>
> That's right. If you think this is crucial, develop a coding standard which
> requires a comment of a certain form, and persuade your development group
> that it's a good idea.

WHY?! (sorry, didn't mean to yell.. this has just been a point all
along). The type cast already exists, it's convenient, it's standardized
already. If there's any reason not to type cast a suspicious pointer, then
the suspicious pointer _should_ _not_ be used in that context.

> > But perhaps programmers don't want this.. it's certainly frightening if
> > they already have LOTS of void pointers all over the place!
>
> Yes, I use pointers to void all the time. Every time I allocate memory, I
> use a pointer to void.

This is much hackneyed. Everytime to copy memory you use a pointer
to void (unless you use a pointer to char, which is the same thing). Every-
time you deallocate memory, you use a pointer to void (well.. free() uses a
pointer to void).

> Every time I store a user-defined type in a generic
> container ADT, I use a pointer to void (and I make that container contain
> *only* objects of that type).

Well then they're not so generic. Nothing is generic until you've
put the kitchen sink and the couch in there.. ;) There are needs for
containers of multi-typed objects, your single-typed containers present
additional overhead in to meet those needs. I would recommend the tech-
nique you suggested which allowed a rudimentary run-typed object, it could
fix this deficiency.

> Every time I write generic container ADTs, I
> make them accept pointers to void.

What men are driven to for want of template container classes.. ;)

> > But then, that should tell them something else.
>
> What's that then?

Well, nothing in your case. Those are appropriate uses for generic
pointers.

> In C, compatible structs are those whose members have exactly the same names
> and types and appear in the same order. Again, you are fundamentally
> confused about C's type system.

Leaving these uncast is inheritantly dangerous, and hardly _easy_
to read,

struct a {
int field11;
};

struct b {
int field11;
};

struct c {
struct a field1;
int field2;
};

struct d {
struct b field1;
int field2;
};

struct b *getB(const char *structname);
:
struct c cstruct;
struct d dstruct;
:
cstruct.a = getB("dstruct");

Good way to confuse the heebie-jeebies out of your newest hire,
leaving that uncast.



> > > > Omitting the cast of a void pointer guarantees nothing, only that the
> > > > programmer is crazy.
> > >
> > > The programmer is crazy to allow the compiler to perform static type
> > > checking? Do you realise how stupid that statement is?
> >
> > My meaning was the programmer is crazy for _performing_ the assignment
> > from a void pointer that he left uncast. If he can cast it, he should.
>
> But casting it prevents type checking, and is therefore crazy.

So, let's assume the programmer doesn't cast it. The compiler
does its normal static type checking on it. The compiler says there is
something wrong with it.

What should the programmer do? To my, perhaps simple, mind, come
only a few possibilities:

1. Cast it.

2. Tell the compiler to shut up by turning off that diagnostic
(please understand that implementations exist that have off switches,
you won't find that in the standard). In this case the programmer
is in boat 1, without the benefits of putting into proper C syntax
what he did.

3. Change the offending line and re-code the solution.

What do you suggest he do? Frankly I favor choice 3, but if he
chooses choice 1, then he's just in the same boat he was in if he had
cast it originally.

Now, the other situation involves a precompiled library for which
the headers exist. Everything won't be well-defined until linking occurs.
In this case we run the same scenario, but without the cast. The programmer
knows about the implementation of a function in the library returning a
void, but he won't cast it (because you don't want him to). Without the
cast it compiles without a hitch. The compiler assumes the functions
prototype is cool, and doesn't care what goes on inside that function to
produce the void pointer it returns.

Life continues for months. Program ran fine without the cast.
Programmer knew to put it in the right struct type pointer. Everything
is peachy. Then another programmer down the hall updates the function
in the precompiled library, and the new version of the library is in-
stalled on everyone's machines. Programs are recompiled to link to the
new static library. Function looks the same prototypically, but its
innards have changed. Now the void pointer it returns is much more
complex in the types it may assume.

This original program runs, sometimes it works, sometimes it
doesn't. Programmers (everyone, nobody even knows that he wrote the
code that caused this yet) are scouring the source code looking for
this new bug.

This bug should stand out upon immediate examination of the
line in question were it to have a type cast from the void pointer
returning library function. Then the type cast could be compared
to the possible types the void that was returned could point to.
However, no type cast was put there. That line looks like every
single other line that calls functions in the newly updated library.
All the programmers know is that it's one of them.

The only way they will locate this bug is by analyzing not
only the offending line which was uncast, but also the lines around
it to determine what the original programmer was thinking about when
he called that function.

Their program probably calls hundreds of other library functions
from that newly updated library, so there's no guarantee that they will
examine that line of code in a timely manner.

The type cast would be a blatant assertion as to the expectation
of the type returned by this library function.

As it turns out, stocks begin trading in only minutes so they
have to do an emergency roll-back to the old version of the library
and pray they can be on-line in time.

The actual analysis to track down the problem might take quite
awhile, involve meetings with the programmers down the hall who maintain
the library, clashes of egos, heated arguments, requiring programmers to
stay after hours away from their families to set up a test bed on the
whole system and get the kinks out.

> Imputing craziness for not liking *your* bizarre coding style is not a good
> way to approach a technical discussion.

That wasn't how I was imputing craziness.. the craziness was the
original dangerous code (for the second time).

Let me burn this down into a crucible from which we may extract the
truth, may we,

1. If the programmer _knows_ _what_ _he_ _is_ _doing_, then the
cast should be made (whether it must be made or is optional
is a matter of personal taste, I don't have a problem with
that, I just say making the cast is better).

2. If the programmer _does_ _not_ know what he is doing, then
the original line should be deleted or redesigned and recoded
until the programmer does know what he is doing (which may
not involve a type cast after all).

I have never suggested programmers apply type casts to _LIE_ to
the compiler, only when they know more than it (and sometimes when they
agree with the compiler, just to document something not immediately
intuitive at first sight).

The immediacy of the information conveyed when a type cast is
present is important. A type cast tells you something _immediately_,
your eyes are still on that line. You eyes do not have to move to
any other line to get that information.

When you are debugging your code, this immediacy is very
helpful. In a large program its not intuitively obvious where to
get your next bit of information. The presence of the typecast
tells you something now.. it almost stands out of the screen and
shouts what it knows to you. If you're debugging a large program,
a lot of the statements start to look alike, these are helpful cues
that shouldn't be understated (their power is readily observed when
compared to a similar large program without the casts, even with
comments, because all the comment blocks begin to look the same,
too).

Casts should also be used infrequently (I know, I say use
them some places they aren't required by law).. the situations when
they come up should be few. They aren't (or should not be) littered
all over a program haphazardly like Parmesan cheese: in those cases
they are being used improperly or the design is unsteady (or in the
very rare case the design is supersteady).

Casts should be used with forethought. I continue to hear
over and over again, "Casts are designed to lie to the compiler."
Peoople have gotten used to using casts to lie to the compiler..
they've fallen into ill repute apparently, but that's no more
their purpose than it is the semi-colon's purpose on the ends of
lines to force you to type a key with your right pinkie!

I could say the only reason for semicolons in C is to get
the semicolon key on the keyboard some keypresses.. that's extra-
ordinarily cynical (and quite silly). Semicolons are used to
terminate statements. It's a matter of perspective, whether we see
them as something useful to the language, or something detrimental.
A lot of people seem to have a detrimental view on type casts.

> > void *vp;
> > char *cp;
> > :
> > cp = vp;
>
> What's your point? This is perfectly correct code, and I (and a lot of
> other people) also consider it good style. If I saw that code with a cast
> on the rhs of the assignment, I'd assume that, like you, the programmer
> didn't understand C's type system.

Ok :/ ... howabout..

void *somethingOrOther(char *arg);
:
void *vp;
vp = somethingOrOther( vp);



> > I like to reuse what C solutions I can in C++,
>
> This explains so much.

Ssurprised? :) I see few particular reasons to leave void pointers
uncast when they are giving something to a typed pointer.. the programmer
should know what the void is for that assignment or the assignment shouldn't
be being made.

Years later, the programmer may come back and not be thinking in the
same vein as he was when he was years longer, and a typecast records that
and refreshes his memory (more or less) instantaneously 9depending on age).

> You are attempting to write in some peculiar hybrid
> of C and C++, in which a pointer to void is compatible with nothing else,

Well, I do regard it as compatible, however I _prefer_ to see a
typecast with it when it's going to something NON-void. The programmer
should know, and the programmer should say so, and if the programmer can't
say so, then the whole thing is shaky to begin with and should be handled
in some other way.

Empower the Programmer!

(The castless creedo seems to be, 'Empower the Compiler...' it has
it's advantages but I prefer the former.)

> and in which pointers to classes with a certain inheritance relation are
> compatible.

Well, C has no such class beast and you haven't heard me raise hide
nor hair of such an animal, have you?

> So please take this crud out of a newsgroup which discusses
> standard C.

It's perfectly permissible and STANDARD C to _use_ typecasts. :)


Derek Harmon

[slightly more sour grapes mercilously deleted and forgotten about.]

Lawrence Kirby

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
In article <35BA59AC...@atlsci.com>
whu...@atlsci.com "William Hughes" writes:

...

>I am now completely lost as to what your point might be.
>

>There are basically three categories of casts.
>
>Casts between non-pointer types:
>

> The most common example is the widely
> used cast of an integer type to a floating point
> type to force floating point arithmetic. The
> cast here is absolutely necessary, and
> there is no other easy way to achieve the same
> result.

You can assign the intermediate result to a temporary floating point
variable. A cast between arithmetic types is just a shortcut here. It is
safe in the sense that appropriate value conversions are always performed,
it doesn't provide a mechanism to defeat the type system.

> There are times when you may want
> to cast between different struct types,
> but if you want to do this you will have to use
> the cast.

That is not possible. You can only cast to scalar types or void. You
can't cast between structure types, only pointer to structure types.

> Everyone seems to be agreed.

Hmmm. ;-)

>Casts between pointers not involving void pointers
>

> If the pointers are of the same type no cast
> is necessary or should be used. If the pointers
> are of different types a cast is necessary and
> should be used.

A cast is not necessary to convert, for example from char * to const char *
which are certainly different types.

> The second case is a signal
> that the code may not be portable or stable.
> The second case should be uncommon in
> good code (many programmers never need
> to cast between two pointer types).

One case where it is generally safe is casting to a pointer to character
type so that, for example, an object can be viewed as a sequence of bytes.
Casting between char * and unsigned char * to access characters and strings
in the appropriate (e.g. for passing to standard library functions) way is
usually safe.

> Everyone seems to be agreed.
>

>Casts between pointers where one pointer is a void *

(Good argument snipped)

--
-----------------------------------------
Lawrence Kirby | fr...@genesis.demon.co.uk
Wilts, England | 7073...@compuserve.com
-----------------------------------------


Aaron Crane

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
In article <35BA9E19...@ix.netcom.com>,

Derek Harmon <de...@ix.netcom.com> writes:
> They do have their uses to tell the compiler the truth about your data
> when you, the programmer, know more than the compiler can.

Of course, if it turns out that the programmer can't tell the compiler
*anything* meaningful about the code, then the programmer loses.

> #include <stdio.h>
>
> struct apple_like {
> int type;
> };
>
> struct macintosh_apple {
> int type;
> int something_mac;
> };
>
> struct windows_apple {
> int type;
> int something_win;
> };
>
> void *returnsPointerToAppleLikeStruct(int a);
> void *vp;
> int type;
> :
> vp = returnsPointerToAppleLikeStruct( 1);
> type = (struct apple_like *)vp->type;

Whoops, you just cast an int to a pointer. Implementation-defined behaviour
when you dereference it.

> :
> if (type == 1)
> printf("%d", (struct macintosh_apple *)vp->something_mac);

Here you cast an int to a pointer, pass that pointer to printf, and make
printf attempt to read that pointer through a va_list as an int. That's
undefined.

> else if (type == 2)
> printf("%d", (struct windows_apple *)vp->something_win);

And again.

> In this example the programmer knows more about vp then the compiler does,

Sadly, in this example, the programmer knows nothing about what he's doing.

And yes, I realise that your argument isn't altered by errors in your
example. I just find it amazing that someone who clearly knows so little
about C could have the audacity to attempt to argue about such subtle
features of the language.

> so the program employs casts on the void pointer to tell the compiler
> _the_ _truth_ about what vp points to.

I haven't yet mentioned that this argument depends on the idiot programmer
concept once again. This design is quite simply broken in C. That is not
an appropriate way to do inheritance, because it is so bug-prone.

The crucial fact is that in C, a pointer to void is a pointer to an `object
of unknown and unknowable type'. It is not the construct on which you build
inheritance. Attempting to pervert it to such a thing is so bug-prone as to
be unmaintainable.

You still haven't shown us any well-written code where a cast to or from
(void *) actually adds anything. It strikes me as particularly stupid to
use an idiot programmer's possible lossages as the way to decide on a code
style, as such programmers are in my experience unlikely to be able to
follow coding guidelines.

Derek Harmon

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
Aaron Crane wrote:
> In article <35BA9E19...@ix.netcom.com>,

> Derek Harmon <de...@ix.netcom.com> writes:
> > They do have their uses to tell the compiler the truth about your data
> > when you, the programmer, know more than the compiler can.
>
> Of course, if it turns out that the programmer can't tell the compiler
> *anything* meaningful about the code, then the programmer loses.

The programmer only loses if he keeps the offending line of code
in the program. My position is that when it turns out that the programmer
can't tell the compiler anything meaningful about the code, then the pro-
grammer should rewrite that code.

> And yes, I realise that your argument isn't altered by errors in your
> example. I just find it amazing that someone who clearly knows so little
> about C could have the audacity to attempt to argue about such subtle
> features of the language.

Ok, I admit I left out the extra set of parens (I did question the
decision then and made a deliberate gamble - which lost).. I normally always
put on the extra set of parens rather than look that precedence rule up.
I understand precedence very well, I just don't have the chart memorized. :)

That is to say, you can write 90% of the code with only 10% of the
rules.. more or less. Why not use Occam's Razor?

But, I wanted to deprive anyone from throwing up the too many
parens means no knowledge of C argument. I failed..

> > so the program employs casts on the void pointer to tell the compiler
> > _the_ _truth_ about what vp points to.
>

> I haven't yet mentioned that this argument depends on the idiot programmer
> concept once again. This design is quite simply broken in C. That is not
> an appropriate way to do inheritance, because it is so bug-prone.

Inheritance? in C?

Meanwhile, yes, I do hold the assertion that it is mostly idiot C
programmers who would write C this way. If they were forced to think the
cast through, maybe they would turn back and try writing a better solution.

Are you completely opposed to the notion that in attempting to
apply a type cast to a particular line of code, perhaps the programmer
will realize the line of code was a bad idea in the first place?



> The crucial fact is that in C, a pointer to void is a pointer to an `object
> of unknown and unknowable type'. It is not the construct on which you build

While it is a void pointer it is unknown and unknowable. At
some point, for some void pointers (those that are not void pointers
which are assigned to and then never assigned from), the pointer
transitions from a pointer to an object of unknown and unknowable
type ... to a pointer to an object whose type _should_ be known
or knowable at that point.

> inheritance. Attempting to pervert it to such a thing is so bug-prone as to
> be unmaintainable.

Well, I can attest to seeing unmaintainable uses such as that.
Yes, they were bug-prone!!! I'm saying they'd be a little easier to
maintain if they had a consistent casting style applied to them (and
perhaps the design would be thrown out when such a casting style
could not be consistently applied).

> You still haven't shown us any well-written code where a cast to or from
> (void *) actually adds anything. It strikes me as particularly stupid to

Let's rehash the hash table example (I won't rewrite it).. you
put things in and you take things out. While they are in the hash table
you don't care what type they are, so you use void pointers. When you
take them out, you are presumably going to use them somehow and should
know what type they are (through whatever mechanism you decide to use,
well-written or not).

Applying the casts presents the programmer's expectations and
even the code's requirements explicitly in the context of the (poten-
tially broken) specific line of code.

It may add knowledge to why the program at large isn't
working correctly. It may add knowledge to why the hash table
library isn't working correctly. The typecast expresses cleanly
these expectations.. when there is a bug, there will be two typecasts
(or a typecast and a declaration) which reveal two requirements in
conflict -- that is the bug. That is what they add.

English is a really bad language for expressing requirements,
that's why you don't program machines in natural languages. C is an
structured imperative language, it can express requirements better
and more uniformly than English.

That is, if the same information revealed by the type cast
were in comments, they may not be as easily compared, evaluated and
understood because of idiosyncracies in people's natural language
wording. When the language has available a construct, which rightly
used (to do what type casts do), has the secondary benefit of clearly
enunciating these requirements, it is preferable to utilize it in
supplementing the less articulate comment (which undeniably has
broader scope in its ability to convey information).

Derek Harmon

Chris Torek

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
(A side note, with a lesson from Real Code. :-) )

>> Casts don't tell the truth or lies, the humans that use them do.

In article <35B96359...@mnsinc.com>


John Kugelman <kuge...@mnsinc.com> writes:
>I have no idea what that means.

It is mainly a rhetorical trick: a true but largely irrelevant
statement, like the old dodge, "Guns don't kill people, bullets
kill people" (which is not true either: the hydrostatic shock,
blood loss, tissue damage, infection, etc., is what kills people).

(Sidebar: if you want to hear *real* masters of rhetorical suasion,
listen to, e.g., California gubernatorial debates, and watch how
they never actually answer any of the questions asked.)

Anyway, back to casts: you *can* "cast truthfully" as well as
"lie with a cast". For instance, any arithmetic type cast means
no more and no less than what it says:

int i;
...
printf("one third of i is %f\n", (double)i / 3);

so we can dispense with these and concentrate on casts from one
pointer type to another.

Consider the following code fragment:

error = bread(devvp, (ufs_daddr_t)(SBOFF/size), SBSIZE, NOCRED, &bp);
if (error)
return (error);
newfs = (struct fs *)bp->b_data;

This reads a "file block" into a "buffer" described by a header
(bp), and puts the read-in data into an array of bytes somewhere
in "well-aligned" memory (bp->b_data). The "file" in this case is
a disk device (devvp), so this amounts to directly reading a disk
block. The code contains an arithmetic cast to (ufs_daddr_t) that
is unnecessary and inappropriate -- bread() has a prototype in
scope, and the type of the logical block number is "daddr_t", not
"ufs_dadddr_t" -- but not actually harmful (the result of SBOFF/size
is a "small number" and ufs_daddr_t and daddr_t are both able to
count to at least 2147483647).

This is followed by some error checking, and then by a pointer
cast. The pointer cast is required because bp->b_data has type
"char *". The buffer contains either a file system super block,
if there is a file system, or garbage data (which will generally
fail subsequent "valid file system" tests). Thus, the cast to
(struct fs *) is a "truthful" cast (modulo possible bogus file
systems). It could -- and probably should, eventually -- be
eliminated by changing the type of "bp->b_data" to "void *", but
there is no big hurry on this.

Compare that with another code fragment:

ufs_daddr_t lbn, nextlbn;
...
else if (lbn - 1 == vp->v_lastr) {
int nextsize = BLKSIZE(fs, ip, nextlbn);
error = breadn(vp, lbn, size,
(daddr_t *)&nextlbn, &nextsize, 1, NOCRED, &bp);
} else
...

This code fragment also contains one pointer cast. The breadn()
function is much like bread(): it reads a disk block into a buffer
and sets the header (here, "bp") to describe the block. In
addition, however, it schedules (starts but does not wait for)
some number of additional disk block reads so as to bring more
of the file into system memory, on the theory that an application
that just read block 17 of file "florp" is soon likely to read
block 18, 19, 20, etc., of file "florp". (The test "lbn - 1 ==
vp->v_lastr" is a simple way to detect sequential access: if we
are reading logical block 17 now, and we last read logical block
16, then lbn-1 == vp->v_lastr == 16, so start fetching block 18.)

Anyway, the breadn() function takes an array of "desired logical
block numbers" and "sizes of each logical block", and a count of
the elements of that array. In this case, we only want to read in
one "next" block as given by "nextlbn" and "nextsize". But we have
a "ufs_daddr_t" -- a logical block address specific to a UFS file
system -- while breadn() takes a "daddr_t", a logical block address
generic to *any* file system.

In this case, the programmer decided to "lie to the compiler": to
tell it that &nextlbn was suitable for use as a "daddr_t *". Now,
suppose ufs_daddr_t is a 32-bit type, but daddr_t is a 64-bit type?
Then, somewhere deep inside breadn(), when we do the equivalent of:

int breadn(struct vnode *vp, daddr_t lbn, int size,
daddr_t *ralbns, int *rasizes, int nreadahead,
struct ucred *cred, struct buf *retbp) {
int i;
...
for (i = 0; i < nreadahead; i++)
start_reading(vp, ralbns[i], rasizes[i]);
}

we will at best pass in a bogus logical block number. At worst,
if daddr_t has more stringent alignment constraints than does
ufs_daddr_t, the attempt to read more_lbns[i] may cause a trap
(leading to a kernel panic).

The fix is simple enough. Instead of:

int nextsize = BLKSIZE(fs, ip, nextlbn);

error = breadn(vp, lbn, size,
(daddr_t *)&nextlbn, &nextsize, 1, NOCRED, &bp);

we can write something like:

daddr_t nextlbn_as_daddr_t = nextlbn;
/* the unweildy name is mainly for illustration */
int nextsize = BLKSIZE(fs, ip, nextlbn);

error = breadn(vp, lbn, size,
&nextlbn_as_daddr_t, &nextsize, 1, NOCRED, &bp);

By removing both the cast and the need for a cast, we fix the bug.

(The broken version "happens to work" because on our systems,
ufs_daddr_t and daddr_t are the same size and representation, as
they were once the same underlying type. The types were split when
converting the system for use on 64-bit architectures. At that
time, a programmer who shall remain nameless inserted the cast,
rather than the correct auxiliary variable. [No, it was not I :-) ])

Chris Torek

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
(I am probably posting way too much verbiage on this, but right now
I am hyped up on corticosteroids for lung inflammation. Sorry about
that. :-) )

In article <35BB81BB...@ix.netcom.com>,
Derek Harmon <de...@ix.netcom.com> wrote:
> Inheritance? in C?

Sure enough -- take a look at my autoconfiguration system for 4.4BSD,
for instance (there is a version in NetBSD, and the FreeBSD folks are
currently looking at putting it into FreeBSD as well).

A quick top-level overview:

- Physical devices exist on a machine.
- Virtual devices exist in a kernel to grant (or sometimes deny)
access to those physical devices.
- We need a way to hook virtual devices to physical devices.
- We need a way to find physical devices at boot- or device-insertion
time (and, on some systems, to "detach" devices, but this is a
much harder problem that is often best solved by "poisoning" the
virtual device attached to a now-vanished physical device).

(This is pretty much orthogonal to the problem of loadable kernel
modules, except for the problems that occur with "detaching". Note
that systems with LKMs use them for non-"device"-y subsystems, such
as file systems or process IPC methods, as well as for device drivers.)

Autoconfiguration can, in most cases, be viewed as a recursive
process dealing with "device trees", by starting at some physical
object (like a bus or adapter card) and fanning out recursively to
those things attached to it, until ultimately we hit some endpoint,
such as a disk drive or video card or serial port.

The kernel side of my autoconfiguration code consists of a
"device" data structure, a "configuration data" data structure,
and some bits of glue code by which machine-dependent code can
do one of two things:

- Announce that a physical device "has been found here". ("Here",
in this case, is "at some other physical device", with a special
case for getting things started.)

- Ask the machine-independent (MI) code to make callbacks to drivers,
enumerating every physical device the system administrator /
designer has told the code "might be found here".

On a PC, for instance, we "find" the "main bus" (the ISA or EISA
or PCI bus) in some machine-dependent fashion. For a PCI bus, for
instance, we then look at all possible PCI slots and declare, for
each slot in which a card is present, that we "found a card". If
the card is, say, a SCSI adapter, its driver configures the card,
and then tries each of the SCSI target numbers to see if a target
is present. On finding a target, it announces that it found a
target; the (MI) SCSI code then probes that target for units, and
announces any units it finds. In this way, we eventually find all
SCSI units at all SCSI targets at all SCSI cards on the PCI bus.

This leads to a parent/child device relationship: aside from the
very top level (e.g., the PCI bus), each device has some "parent
device" to which it attaches, and each device has potentially many
"child device"s that attach to it.

At the MI level, each device is represented by a "struct device",
which currently looks like this:

struct device {
enum devclass dv_class; /* this device's classification */
struct device *dv_next; /* next in list of all */
struct cfdata *dv_cfdata; /* config data that found us */
int dv_unit; /* device unit number */
int dv_flags; /* flags (copied from config) */
char dv_xname[16]; /* external name (name + unit) */
struct device *dv_parent; /* pointer to parent device */
};

(Children are not enumerated directly at the MI level, but by
using the "all devices" list, it is possible for MI code -- normally
outside the kernel -- to build the "actual device tree" using the
parent pointers.)

Now, a "struct device" is not sufficient to represent, e.g., a SCSI
adapter card, so each card has a different structure:

/* simplified */
struct scsi_hba {
struct device hba_dev;
struct scsi_driver *hba_driver;
struct scsi_target *hba_targets[8];
... stuff for dealing with queueing, stuck cards, etc ...
};

Next, we have an instance of an actual SCSI adapter, in this case,
the "esp" adapter on a SPARC SBus:

struct esp_softc {
struct scsi_hba sc_hba;
...
volatile struct espreg *sc_esp;
...
};

Here is how the inheritance works. The "esp" driver has two
configuration-related entry points, espmatch() and espattach().
The first of these (espmatch()) simply makes sure that the
autoconfiguration code really is dealing with an "esp" SCSI adapter
and not something that "looks similar, but is different". (This
is more useful on things like the PCI bus, where you match cards
by "manufacturer ID number", but it also allows one to code special
drivers for "broken" devices, without polluting the mainstream
driver code. We have not had to use this other feature yet.)

When we do find an "esp device", we allocate sizeof(struct esp_softc)
bytes. The espattach() function then looks somewhat like this
(this is, alas, enormously simplified):

void
espattach(struct device *parent, struct device *self, void *aux) {
struct esp_softc *sc;
struct sbusaux *sa;
extern struct scsi_driver esphbadriver;
...
sa = aux;
sc = (struct esp_softc *)self;
sc->sc_esp = (volatile struct espreg *)mapreg(&sa->...);

... /* Set up the device and start probing for targets. */
sc->sc_hba.hba_driver = &esphbadriver;
scsi_probe_targs(&sc->sc_hba);
}

Remember that the esp_softc begins with a scsi_hba, and the scsi_hba
begins with a device -- so at various points, we can extract the
"device" part, or the "hba" part, or go from any of those back to
the entire "esp_softc" structure.

These, of course, do require casts, because none of these has type
"void *". This is an example of how to do single inheritance in
ANSI C -- in C++ or Plan-9-C, one can do this without casts, but
in ANSI C, we use the guarantee that a pointer to an initial
structure element is suitable for conversion to a pointer to the
whole structure.

Note that the "aux" argument here *is* "void *"; it represents some
machine-dependent data, in this case, SBus information that is used
to find the hardware registers on the esp card. (In fact, this
particular data structure itself uses structure inheritance, in
a simpler form: it consists of generic "sparc autoconf information"
followed by the SBus slot and offset. The latter are mainly historical
since new SBus cards can have multiple offsets. The aux information
includes a pointer to a "sparcbus" that is in turn an inheritable
structure with a type, a register mapping function, an interrupt
hook, and possible additional private data. For the SBus and OBIO,
the additional data is a set of "ranges" that hold physical I/O
mapping information. Not that any of this is particularly relevant
to the problem at hand, although it does illustrate how inheritance
can run away with you.)

Getting back to "void *" and casts:

> Let's rehash the hash table example (I won't rewrite it).. you
>put things in and you take things out. While they are in the hash table
>you don't care what type they are, so you use void pointers. When you
>take them out, you are presumably going to use them somehow and should
>know what type they are (through whatever mechanism you decide to use,
>well-written or not).

To wit, suppose we have a name/value hash table, with code such as
this (from my own code implementing some of the user-level parts
of autoconfiguration):

struct hashent {
struct hashent *h_next; /* hash buckets are chained */
const char *h_name; /* the string */
u_int h_hash; /* its hash value */
void *h_value; /* other values (for name=value) */
};
struct hashtab {
size_t ht_size; /* size (power of 2) */
u_int ht_mask; /* == ht_size - 1 */
u_int ht_used; /* number of entries used */
u_int ht_lim; /* when to expand */
struct hashent **ht_tab; /* base of table */
};

[init, insert/replace, and other miscellaneous code omitted]

[N.B.: we get to use pointer equality to test string equality
here because of other, omitted code that turns all strings into
Lisp-like "atoms". This saves lots of runtime space and time
because "config" tends to deal with lots of repeated words. For
instance, the actual config lines for an "esp" are:
espdma* at sbus? slot ? offset ?
dma* at sbus? slot ? offset ?
esp* at sbus? slot ? offset ?
esp* at espdma?
esp* at dma?
because of wiring changes Sun made over the years.]

void *
ht_lookup(ht, nam)
register struct hashtab *ht;
register const char *nam;
{
register struct hashent *hp, **hpp;
register u_int h;

h = hash(nam);
hpp = &ht->ht_tab[h & ht->ht_mask];
for (; (hp = *hpp) != NULL; hpp = &hp->h_next)
if (hp->h_name == nam)
return (hp->h_value);
return (NULL);
}

> Applying the casts presents the programmer's expectations and
>even the code's requirements explicitly in the context of the (poten-
>tially broken) specific line of code.

Now let me show a few examples of actual calls to ht_lookup:

/*
* Called when evaluating a needs-count expression. Make sure the
* atom is a countable device. The expression succeeds iff there
* is at least one of them (note that while `xx*' will not always
* set xx's d_umax > 0, you cannot mix '*' and needs-count). The
* mkheaders() routine wants a flattened, in-order list of the
* atoms for `#define name value' lines, so we build that as we
* are called to eval each atom.
*/
static int
fixcount(const char *name, void *context)
{
struct nvlist ***p = context;
struct devbase *dev;
struct nvlist *nv;

dev = ht_lookup(devbasetab, name);
if (dev == NULL) /* cannot occur here; we checked earlier */
panic("fixcount(%s)", name);
nv = newnv(name, NULL, NULL, dev->d_umax);
**p = nv;
*p = &nv->nv_next;
(void)ht_insert(needcnttab, name, nv);
return (dev->d_umax != 0);
}

Your assertion is that this code is somehow clearer if we replace
the first executable line with:

dev = (struct devbase *)ht_lookup(devbasetab, name);

This is of course a matter of opinion -- but I happen to believe
yours is wrong, that the cast does not add what I would call "useful
redundancy".

> It may add knowledge to why the program at large isn't
>working correctly. It may add knowledge to why the hash table
>library isn't working correctly. The typecast expresses cleanly
>these expectations.. when there is a bug, there will be two typecasts
>(or a typecast and a declaration) which reveal two requirements in
>conflict -- that is the bug. That is what they add.

The problem here is that any *actual* bug is likely to be, e.g.,
"devbasetab is supposed to contain only `devbase's, but someone
stored a `devi' instance, rather than a base".

The redundant cast is just that -- redundant. We can see immediately
from context that "dev" is a "struct devbase *". If there is a
conflict -- if someone writes, say:

dev = (struct devi *)ht_lookup(devbasetab, name);

we do get a compiler-time error, but all we do about it is look
at the obvious ("gee, dev is a "devbase" and this is the "devbasetab",
he must want to get a devbase out") and delete the bogus cast.

Most of the calls to ht_lookup are in similarly simple functions.
The most complicated function -- probably the worst in the entire
program, as it implements the heart of what it means to attach one
device to another -- reads, in its entirety:

/*
* Add the named device as attaching to the named attribute (or perhaps
* another device instead) plus unit number.
*/
void
adddev(const char *name, const char *at, struct nvlist *loclist, int flags)
{
struct devi *i; /* the new instance */
struct attr *attr; /* attribute that allows attach */
struct devbase *ib; /* i->i_base */
struct devbase *ab; /* not NULL => at another dev */
struct nvlist *nv;
const char *cp;
int atunit;
char atbuf[NAMESIZE];

ab = NULL;
if (at == NULL) {
/* "at root" */
if ((i = getdevi(name)) == NULL)
goto bad;
/*
* Must warn about i_unit > 0 later, after taking care of
* the STAR cases (we could do non-star's here but why
* bother?). Make sure this device can be at root.
*/
ib = i->i_base;
if (!onlist(ib->d_atlist, NULL)) {
error("%s's cannot attach to the root", ib->d_name);
goto bad;
}
attr = &errattr; /* a convenient "empty" attr */
} else {
if (split(at, strlen(at), atbuf, sizeof atbuf, &atunit)) {
error("invalid attachment name `%s'", at);
/* (void)getdevi(name); -- ??? */
goto bad;
}
if ((i = getdevi(name)) == NULL)
goto bad;
ib = i->i_base;
cp = intern(atbuf);
if ((attr = ht_lookup(attrtab, cp)) == NULL) {
/*
* Have to work a bit harder to see whether we have
* something like "tg0 at esp0" (where esp is merely
* not an attribute) or "tg0 at nonesuch0" (where
* nonesuch is not even a device).
*/
if ((ab = ht_lookup(devbasetab, cp)) == NULL) {
error("%s at %s: `%s' unknown",
name, at, atbuf);
goto bad;
}
/*
* See if the named parent carries an attribute
* that allows it to supervise device ib.
*/
for (nv = ab->d_attrs; nv != NULL; nv = nv->nv_next) {
attr = nv->nv_ptr;
if (onlist(attr->a_devs, ib))
goto ok;
}
attr = &errattr;/* now onlist below will fail */
}
if (!onlist(attr->a_devs, ib)) {
error("%s's cannot attach to %s's", ib->d_name, atbuf);
goto bad;
}
}
ok:
if ((i->i_locs = fixloc(name, attr, loclist)) == NULL)
goto bad;
if (i->i_unit == STAR && ib->d_vectors != NULL) {
error("%s's cannot be *'d as they have preset vectors",
ib->d_name);
goto bad;
}
i->i_at = at;
i->i_atattr = attr;
i->i_atdev = ab;
i->i_atunit = atunit;
i->i_cfflags = flags;
selectbase(ib);
/* all done, fall into ... */
bad:
nvfreel(loclist);
return;
}

This code contains no pointer casts, and I believe it is best this
way. Any redundancy you add by a cast tells you only what is
(perhaps not *quite* so clearly) visible at the top of the function.
(A good programmer's editor will let you look at the top of the
function and the body of the function at the same time.)

In other words, this is a case of "bad redundancy": most languages
(natural and computer) require a certain amount of redundancy,
because redundancy does in fact catch errors. In natural language,
when you say the same thing twice in two *different* ways, you
emphasize what you have said. On the other hand, when you use the
same *words* twice in a row, most people would think you have made
a mistake. On the other hand, when you use the same *words* twice
in a row, most people would think you have made a mistake. [ :-) ]

I believe this remains true in programming languages. Repetition,
particularly with rephrasement, (a) adds emphasis and (b) catches
errors -- but with casting "void *", what you generally get is
"redundancy" rather than "repetiton". Moreover, the conversion
from "void *" to "struct devbase *" (or whatever) is not a particularly
interesting occurrence, and as such, it does not *deserve* emphasis.

For an example of "good redundancy", I think we need to turn to
languages with "implicit variable declaration". Old FORTRAN is
the classic example:

INTEGER FUNCTION ZEROS (A, I, J)
INTEGER A(I, J)

N = 0
DO 10 JJ = 1, J
DO 10 II = 1, I
IF (A(II, JJ) .EQ. 0.0) M = N + 1
10 CONTINUE

ZEROS = N
RETURN
END

In this case, it is easy to misspell something, as I did deliberately
above. Until we find the typo, we are left wondering why ZEROS
always insists there are no zeros in any array. This particular
mistake is a little more difficult to make in C than in FORTRAN,
even when we maintain the execrable names:

int zeros(int **a, int i, int j) {
int ii, jj, n;

n = 0;
for (ii = 0; ii < i; ii = ii + 1)
for (jj = 0; jj < j; jj = jj + 1 )
if (a[ii][jj] == 0)
m = n + 1;
return n;
}

This draws a diagnostic to the effect that "m" is undefined. Of
course, since C allows expressing "n = n + 1" as the less repetitious
"n++", the problem is even less likely to occur in practise.

Now, if casting "void *" frequently turned up typos, I would argue
the opposite side. My personal experience is that it does not.
In short, I find no value to writing:

dev = (struct devbase *)ht_lookup(devbasetab, name);

instead of:

dev = ht_lookup(devbasetab, name);

Of course, this is why I wrote the latter in the first place. :-)

William Hughes

unread,
Jul 26, 1998, 3:00:00 AM7/26/98
to
Derek Harmon wrote:

> Any shaky type cast for certain deserves a comment somewhere near,
> but searching on comment text is a much less consistent tool to rely on
> when you are charged with maintaining 100,000 lines of source code.
>

Well, with some exceptions (don't you hate it when Lawrence Kirbycriticize a post
and you can't do any thing about it because
he's right) if you are not doing something "shaky" you don't
need the cast.

If we could rely on comments the whole argument is moot.
The needed information about any cast would be in
the comments.

We can't rely on comments. The question becomes:
is the self documentation you get from the casts worth
masking dangerous contructs. In my view (shared
with many others) the self documentation is not worth it.

> First, I appreciate the time you've taken William, to be an
> 'impartial' arbitor in assembling all of this in one place.
> Thank you. :)
>

Your welcome. On the other hand I only do this as longas I find it fun. And I am
anything but an `impartial'
arbitor (quotation marks or not). I am very much
an anti-cast partisan.

-William Hughes


Ed Breen

unread,
Jul 27, 1998, 3:00:00 AM7/27/98
to
Lawrence Kirby wrote:
>
> A cast is not necessary to convert, for example from char * to const char *
> which are certainly different types.

True, and although unwise, but the only way to go the other way
is through a cast, as const-quailfied types maybe
stored in read only memory.

--
Ed. Breen
Email: Ed.B...@Altavista.net.JUNK
(http://www.eicc.home.ml.org)
-----------------------------------------

Lawrence Kirby

unread,
Jul 27, 1998, 3:00:00 AM7/27/98
to
In article <35BA2AF1...@ix.netcom.com>
de...@ix.netcom.com "Derek Harmon" writes:

>John Kugelman wrote:
>> You're proving tiring. Show me of an example where a void* pointer is
>> used that has an identifiable type but cannot be replaced by that type.
>> You are making me believe that you don't really understand the purpose
>> of void* pointers, which is to hold generic, typeless data. void* HAS
>> NO TYPE. If your pointer, don't use void*.

void * is a perfectly good object type (as are all pointer types). It is
just the the type of object it points to is not known (at least not from
the pointer type).

> Just let me make my point clear, eventually the pointer _will_
>_have_ a type.

IOW to access what the pointer points to you have at some point to convert
the pointer to one of the appropriate pointer to object type, either
directly or implicitly using a standard library function such as memcpy().

> It will receive this from a void* or some other * not
>of the same type. Then it must be type cast.

It must be type cast if the source and target types are not assignment
compatible. If they are there is no requirement to use a cast.

> You have the option of
>not type casting, and may or may not (char*s and things the compiler
>may see as compatible) get a warning. Whether it produces a warning
>or not (it _usually_ will), it should be cast. Here's a "Re-Hash"...

No, the standard is quite clear on which pointer conversions can be
performed by simple assignment and which cannot. Those that cannot require
a cast. Those that can don't require a cast and the point being made is that
using a cast in those situations is usually a bad idea.

> void *hash_table[101];
>
> int hash(const char *name) {
> if (name) {
> return name[0] % 101; /* pathetic hash fn, I know */
> } else return -1;
> }
>
> void *hash_lookup(int key) {
> return ((key < 0) || (key > 100)) ? NULL : hash_table[key];
> }
>
> void *hash_lookup_by_name(const char *name) {
> return hash_lookup( hash(name));
> }
>
> This sort of example has been floating around the thread..
>
> So, suppose I put apples and oranges into the hash ADT.. now I
>want to get them out by the name of the fruit ("Tangerine Orange",
>"Macintosh Apple", "Big Red"). When I get them out I want to display
>a message box depending on the type, and I should be able to determine
>their type when I retrieve their void pointer.

How will you do that? The method will have an impact on the rest of the code.

> In assigning that
>pointer anywhere in my functions to display fruit, I want to cast it
>to (struct apple *) or (struct orange *).

What you need to show is an example of this sort of code.

> Someday I may add more fruit. Suppose I add tomatoes, then I
>end up getting a hash table collision for "Big Red" tomatoes. If I
>add tomatoes to the display fruit function, I want to cast it as
>(struct tomato *).
>
> Now eventually I'm going to debug this after Big Red comes up
>displaying an tomato message box when I expected an apple message box,
>since the tomato struct overwrote the apple struct of the same name,
>effectively, in the hash table. This will be easier if there are
>typecasts on the pointer that was void when it as sonn as possible
>after its return from the hash function. If you make the assignment
>from a void pointer to a typed pointer, you must know at that point
>what the types are.

What's important here is appropriate variable naming, so that you can see
that the value is being assigned to a variable suitable for the purpose.

...

> Besides, if a person cuts and pastes large tracts of code with
>minor alterations required between replications (a practice that has
>been brought up as a reason not to cast because it requires more work),
>and you're in a hurry (as I said, a reason not to cast because it
>requires more work, which implies you are in a hurry).. then leaving
>out one alteration in thirty replications is quite possible (as is,
>in fact, leaving out the same alteration on all thirty replications).
>
> This is why cutting and pasting code can be dangerous.. such
>typos frequently go to compilation to be detected.

Cut and paste can certainly be dangerous. Using explicit casts are not going
to fix that, they just add something else that needs to be changed.

>> "Protecting against typos" is a stupid reason to add
>> a cast. You should add a cast if you need its semantics in your code
>> (which may be never in strictly conforming code), not to protect
>> yourself.
>
> Every little bit of syntax protection is helpful. The cast makes
>the compiler check that the rhs and lhs are the same type.. they should be.

The problem is that casts don't protect, they do quite the opposite, they
reduce any guarantee you have that the value on the right (which then
becomes the operand of the cast) has an appropriate type to be sensibly
converted to the type on the left. They also clutter up code making it
less readable.

> Protecting themselves is the major reason I hear for not casting
>malloc, protect me from forgetting to include stdlib.h. Then they go to
>C++ with that solution and have extra work with it,

Why are you converting the code to C++ if not to make use of C++'s extra
facilities? That being the case then each case of this sort of thing needs
be be checked and converted to the appropriate C++ idiom. Therefore having
the C++ compiler complain about uncast conversions is a positive advantage.
If you don't want to convert the code then just continue to compile it as
C and use C++'s extern "C" linking mechanisms in any other C++ code you
develop for the application. The decision to convert code from one language
to another should not be taken lightly.

>or they cut & paste
>plenty of mallocs and they make a typo or forget an alteration.

Evidence? Or are you just fabricating a straw-man?

>> > This nefarious malloc() typo is also well known to strike in
>> > replicated code! You do some malloc's in one function and want to cut
>:
>> Shouldn't you been using templatized cut and paste or a Perl script for
>> this?
>
> Templatized cut and paste: yes, now; didn't have that years ago
>and I remember something similar to that happening on occassion. Besides,
>people keep raising replicated code as an argument against casts, its an
>argument for them as well. It's really a wash.. and my opinion is repli-
>cated code is just as dangerous as the pointer assignments that must be
>cast, so I try and avoid them.

That's fair enough.

...

>> That's not what a cast operator is for! Get that through your head. A
>> cast is used to change the type of an object or value to another, not to

In fact casts only operate on values to produce a value result.

> This is a good definition of cast, go with that. If you're
>reading source code the use of a cast should indicate something to you.

Yes, it indicates different things in different cases. Arithmetic casts
simply indicate that a necessary conversion is taking place. Pointer casts
draw attention to what is likely to be an abnormal or potentially
dangerous or non-portable construct (with a few specific exceptions).

> The same as,
>
> int func(int a, int b);
>
> Tells you a poorly named function returns an int, and takes two
>poorly named integer arguments. You don't need a comment for that. >

Right, correct and fill declarations are excellent documentation. That
also applies to the pointer variable you are assigning to with something
like

int *iptr;

...

iptr = vptr;

What you're claiming is that it is better to call func with

long l;

l = (long)func((int)a, (int)b);

i.e. adding additional casts even though the declarations provide all
of the necessary information.

> A well-written C program largely documents itself.

Of course, but there is good documentation and bad documentation. If the
documentation clutters the code and obscures the important operations then
it is bad documentation.

Derek Harmon

unread,
Jul 27, 1998, 3:00:00 AM7/27/98
to
Lawrence Kirby wrote:

> de...@ix.netcom.com "Derek Harmon" writes:
> > It will receive this from a void* or some other * not
> >of the same type. Then it must be type cast.
>
> It must be type cast if the source and target types are not assignment
> compatible. If they are there is no requirement to use a cast.

Agreed, I was getting overzealous in that sentence to explain
what must happen in my casting style (because I think it to be a good
idea at that point to highlight the assignment). I didn't mean to imply
the C language requires a cast there.

:


> > This is a good definition of cast, go with that. If you're
> >reading source code the use of a cast should indicate something to you.
>
> Yes, it indicates different things in different cases. Arithmetic casts
> simply indicate that a necessary conversion is taking place. Pointer casts
> draw attention to what is likely to be an abnormal or potentially
> dangerous or non-portable construct (with a few specific exceptions).

Those few specific exceptions are the points which justify the
use of an explicit casts; in the abnormal conditions the programmer really
needs to reassess the whole matter of the line of code, much less the type
cast to put there. I don't usually cast pointers myself even using this
style I've described: yes it is rare, it should be rare! Occassionally
I may cast more often than others.. c'est la vie. :)

We can see two different examples,

Cast-less Programming Cast-ful Programming

char *cp; char *cp;
int *ip; int *ip;
void *vp; :
: :
vp = ip; cp = (char *)ip;
cp = vp;


Both of these snippets accomplish the same thing, and if for
whatever reason you want to go through an int one byte at a time without
regard for endianess they may be called upon. I just personally think
the cast-ful example on the right expresses the assignment better (an
opinion), while the one on the left isn't as immediately clear.

> What you're claiming is that it is better to call func with
>
> long l;
>
> l = (long)func((int)a, (int)b);
>
> i.e. adding additional casts even though the declarations provide all
> of the necessary information.

This is not what I'm claiming (some people have asserted I claim
that, but they haven't read the original post or the Subject line).

> > A well-written C program largely documents itself.
>
> Of course, but there is good documentation and bad documentation. If the
> documentation clutters the code and obscures the important operations then
> it is bad documentation.

Good enough, to each their own judgement of good and bad
documentation. :)

Derek Harmon

Szu-Wen Huang

unread,
Jul 28, 1998, 3:00:00 AM7/28/98
to
Derek Harmon (de...@ix.netcom.com) wrote:
: Szu-Wen Huang wrote:
[...]

: > When you write:
: >
: > p = malloc(...);
: >
: > the type of malloc() is unimportant, because the compiler makes sure
: > that it's compatible with the type of p. IOW, all you need to do is to
: > look up how p is declared.

: What about all the other places you're using a void *? :)
: Often, a void pointer is passed into a function and it's much harder
: than just looking up a nearby declared type to identify it.

You mean like this?

void foo(void *p)
{
int *q;

q = p; /* versus "q = (int *) p;" */
}

I look up the *left-hand side*.

[...]
: > Except if you had written:


: >
: > a = malloc(sizeof *a);
: >
: > you wouldn't have either problem.

: Except if I had "typed it correctly" you mean (in your preferred
: format, which is an acceptable style choice, but can be just as easily
: mistyped):

: s = malloc(sizeof *a);

: or

: a = malloc(sizeof *s);

: No warning, and just as hard to distinguish on my terminal (eyes
: could use a larger font). The protection is against TYPOS, you don't
: know when they are going to happen. You do know when you use malloc(),
: so you know to include stdlib.h. A typo can hit anywhere.

How would the above differ from the probability of this typo:

struct a_type *a;
struct s_type *s;

a = (struct a_type *) malloc(sizeof *s);

If anything, there's more to read and *miss*.

[...]
: > 1. To know the type of the right-hand side, look up the left-hand
: > side, for the compiler guarantees they are compatible.

: If it's a void pointer the compiler can only say its suspicious.

And what competent programmer doesn't fix something suspicious?

: > 2. To document, use a comment.

: It's so un-C like to use a comment to document something C
: itself includes an operator for.

You are using something with a real function in a place where it has
none as a comment. That's philosophically akin to:

a = +1;

to show that a can only be positive.

[...]
: You've claimed typecasting these pointer operations reveals a


: lack of competence in and confidence with C. When C itself offers a
: syntactic unit which can be used to express ("document/guarantee/warn

: me if not so/the types on both sides are this or compatible to this")


: elegantly and fluently in the C language.. you claim it shouldn't be
: used?

No, I did not mean (I don't know the exact words that led you to this
impression) that anybody who typecasts these are incompetent. What
I meant was that I couldn't tell one from another. I can't tell if
a programmer put it there as a comment or to shut the compiler up
about a bug he didn't understand.

: > 3. If the RHS changes type during maintenance, casting will shut
: > the compiler up, while not casting generates a compile-time error.

: If you didn't cast originally, then either its not a void pointer
: assignment or you ignored the compile-time error then. If the RHS is a
: void pointer, its type could change during maintainence in any other
: module that the one being compiled.

What I meant was, without a cast, the righthand side type can change
from a compatible type to a void * and back, but *not* to an incompatible
type without error. With a cast, you miss the error.

: > 4. If the LHS changes type, p = malloc(sizeof *p) automatically
: > adjusts.

: Agreed. That was never contested. This has nothing to do with
: protecting against typos (because the line doesn't contain a typo).

Neither does your cast.

: > True, but a typecast operator is dubious by *nature*. Its purpose to
: > the language and compiler is to override the default typechecking
: > mechanism. That's why it's a bad thing to sprinkle unnecessarily.

: It does that, it also documents that you wanted to override the
: default typechecking mechanism, and it prominently places into view the
: required type at that point in the program.

*IF* I can assume that the original programmer meant it. That's a big
"if".

: > : > Yes, actually, and I find myself wondering if the programmer knew his/


: > : > her stuff when I see a cast *operator* doing what a comment should.
: >
: > : That's what that operator is for.
: >

: > No, it isn't. The cast operator is essential to the C language, but
: > cast comments are not.

: The cast operator is essential for what? Casting void pointers?
[...]

The cast operator is essential to inform the compiler that two
apparently incompatible types are in fact compatible. It is an
"I know better, shut up" mechanism, and is obviously dangerous
in the wrong hands.

If I see a program with minimal casting, I know as fact that the
programmer knows his types. If I see a program with superfluous
casting, I can't decide if it's just you or somebody who doesn't
know C.

Lawrence Kirby

unread,
Jul 29, 1998, 3:00:00 AM7/29/98
to
In article <35BB81BB...@ix.netcom.com>
de...@ix.netcom.com "Derek Harmon" writes:

...

> Meanwhile, yes, I do hold the assertion that it is mostly idiot C
>programmers who would write C this way. If they were forced to think the
>cast through, maybe they would turn back and try writing a better solution.

In my experience people who write code like this in the first place
have no qualms about using casts and I doubt whether an explicit cast
would make them rethink anything.

The main use for conversions to and from void * is around function calls
where the function tries to provide some sort of generic object interface
(e.g. malloc, memcpy, fread, qsort from the standard library). Do you
cast to void * when for example passing arguments to memcpy?

Another fairly common use is for a similar purpose in datastructures so you
might, for example, have void * structure members. However even here the
conversion to and from void * is generally best done around a function
interface.

The examples presented so far have involved casting for isolated assignments
which I don't think give a realistic picture.

Antti-Juhani Kaijanaho

unread,
Jul 31, 1998, 3:00:00 AM7/31/98
to
Derek Harmon <de...@ix.netcom.com> writes:

> Besides, if a person cuts and pastes large tracts of code with
> minor alterations required between replications

... then he or she should be shot at sight. If you find you are
cutting and pasting code, make it a separate subprogram (function in
C) you call from everywhere else. The minor alterations are taken
care of by giving the function proper parameters.


Antti-Juhani
--
Antti-Juhani Kaijanaho <ga...@iki.fi> ** <URL:http://www.iki.fi/gaia/> **

I can't seem to find a lowercase 'r' on my keyboard.
(Lee Davies in comp.unix.programmer on July 22, 1998)

Chris Torek

unread,
Aug 2, 1998, 3:00:00 AM8/2/98
to
In article <35BA6627...@ix.netcom.com> Derek Harmon
<de...@ix.netcom.com> persists in the pointless casting:
>... now, if I had used a cast as follows,

>
> a = (struct apple *)malloc(sizeof *a);
> a = (struct orange *)malloc(sizeof *o);
>
>Then the compiler would check the second line and see that the lhs (type
>struct apple pointer) isn't the same as the rhs (type struct orange
>pointer) and it would issue a diagnostic.

Yes, and if you wrote:

a = (struct apple *)malloc(sizeof *o);

the compiler would see that the types matched and *not* complain,
and then where would you be?

If you want to make sure you did not make any typos, why not write
each program twice, separately and on different days, and then
compare the two programs ? I bet you will catch far more typos that
way, including those that would get past the compiler. If you
want to repeat yourself, why not *really* repeat yourself?

0 new messages