Common misconceptions about C (C95)

L

Lew Pitcher

Here's why I (tend to) cast.

If I have some code that looks like this

//
-------------------------------------------------------------------------
float *foo; size_t n_foo;

/*
.
. ...some code here...
.
*/

foo = malloc(n_foo * sizeof(float)); /* (1) */
//
-------------------------------------------------------------------------

Now, some time later, I decide that floats aren't giving me the accuracy
I require, or I find a way to gain more contiguous memory, so I decide
that I want to use doubles, not floats...

so "float *foo" becomes "double *foo", but my compiler is powerless to
help me find things like (1), that are now distincly bugs.

The alternatives are

foo = malloc(n_foo * sizeof(*foo));

(which works in C but won't compile as C++) or

So? *First* decide the language you will write your code in, *then* design
your code. If you choose C, write C. If you choose some other language,
write in that language.

/If/ you choose C, then
foo = malloc(n_foo * sizeof(*foo));
is the proper way to write the statement so that you can later change the
type of "foo" without (a) having to revise this statement, and (b) masking
other problems (like missing #include statements).
 
L

Lew Pitcher

Keith Thompson said:
The real reason not to cast the result of malloc is that *the cast
has no benefit*

Here's why I (tend to) cast.

If I have some code that looks like this

//
-------------------------------------------------------------------------
float *foo; size_t n_foo; [snip]

foo = malloc(n_foo * sizeof(float)); /* (1) */
//
-------------------------------------------------------------------------

Now, some time later, I decide that floats aren't giving me the accuracy
I require, or I find a way to gain more contiguous memory, so I decide
that I want to use doubles, not floats...

so "float *foo" becomes "double *foo", but my compiler is powerless to
help me find things like (1), that are now distincly bugs.

The alternatives are

foo = malloc(n_foo * sizeof(*foo));

(which works in C but won't compile as C++) or

I, for one, anxiously await the day when a C/C++/COBOL compiler is finally
released. That way, I can complain in comp.lang.c that
foo = malloc(n_foo * sizeof(*foo));
"works in C, but won't compile as COBOL" ;-)
 
K

Keith Thompson

Gareth Owen said:
Keith Thompson said:
The real reason not to cast the result of malloc is that *the cast
has no benefit*

Here's why I (tend to) cast.

If I have some code that looks like this [...]
foo = malloc(n_foo * sizeof(float)); /* (1) */
// -------------------------------------------------------------------------

Now, some time later, I decide that floats aren't giving me the accuracy
I require, or I find a way to gain more contiguous memory, so I decide
that I want to use doubles, not floats...

so "float *foo" becomes "double *foo", but my compiler is powerless to
help me find things like (1), that are now distincly bugs.

Right, so don't do that.
The alternatives are

foo = malloc(n_foo * sizeof(*foo));

(which works in C but won't compile as C++) or

Yes, if you need to compile the same code as C and as C++, then you
do need the cast.

And if you're using a system that doesn't support the '[' and ']'
characters, then you need to use trigraphs. Do you use trigraphs in
all your code, just in case?

Do you *really* need to compile the same code as C and as C++?
Why not just compile it as C and, if necessary, link it together
with your compiled C++ code, using C++'s cross-language facilities?
foo = (float *)malloc(n_foo * sizeof(float));

which is a compile time error in C or C++.
The former is better, but the second is better than the uncast version.
Working > compile error > silent bug.

I concede it's not a huge benefit, but its not no benefit.

It's no benefit unless you're writing bilingual code.
 
R

Richard Tobin

spinoza1111 said:
It is then followed by what I call lazy evaluation: in parsing when a
constant operand is discovered that is the unity or zero element of
its "group" (1, 0, false, or true) then we do not bother to generate
code for the binary operation it is input to when that binary
operation has no effect.

The term "lazy evaluation" already has a well-established meaning in
computer science, and that isn't it. It refers to deferring the
evaluation of an expression until its value is needed. One of its
interesting uses is that it allows you to build infinite data
structures, part of which you would presumably eventually evaluate
to (for example) print it out.

-- Richard
 
R

Richard Tobin

All well and good, but it singularly doesn't apply to void*.

The traditional bug that not casting malloc() catches is when malloc()
is not declared (because the relevant #include is omitted) and thus
defaults to returning int. C's strong typing would normally catch
that error, but is defeated by the cast. That was why I used "int" in
my example.

When you haven't made that mistake, the cast makes no difference
either way in terms of type safety.

It also seemed to me that Spinoza was asserting that casts are in
general a way to enforce strong typing, and I wanted to refute that.

-- Richard
 
H

Herbert Rosenau

10. Use of malloc without casting is considered a poor practice (it is an
error in C++). Cast it to suitable type instead.

C is NOT C++.

It is considered errornous to cast the result of any function that
returns a pointer. That includes malloc, calloc, realloc because it
will produce bugs.

There is one exception: you use a K&R 1 compiler. But since C is a
language that knows prototypes it is clearly a bug to cast pointers
returned by a function. This faulty cast may end up in undefined
behavior.

Include the header that declares the prototypes.

In K&R 1 any function was returned int when nothing else was declared.
Since more than 15 years C was changed to have prototypes that will
declare what type comes back from a function - and since then it is
clearly bad practise to override the prototype with a faulty cast.

So never ever use x = (<type>) malloc(size); because when you goes
wrong by not #include <stdlib.h> you fails to serve the compiler the
right type malloc returns - that can end in converting an int that
malloc has not even set to an pointer because default converation says
that malloc returns int, not a pointer. That means that on some
implementations not the returned pointer but an int returned some time
prior by another function will get as source for cast.

So since that more than 15 years it is good practise to use prototypes
and never ever cast the returnvalue of functions returning some other
value than int.


--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

You are not allowed to discuss C99 in CLC...

Bullshit. This shows that you are nothing else than a twit.

It's now significantly more than 15 years that C knows about
prototypes and only twits are unable to accept that.

Even with C89 prototypes are well known and casts are errornous to
replace them.

No, don't tell about the other language named C++ - tthere is nothing
that says C++ have to use the malloc family - except twits again.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

Sounds to me idiotic not to cast. Strong typing better than weak
typing. But the cool, studly kids don't cast. I cast because I'm good
at C and I don't even use it much (which is a sign of a programmer
who's good at C, paradoxically enough).

We know already that you are unable to learn C. You're simply too
ddumb to get any language right.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

Using malloc with a cast may show a lack of insider knowledge of a
broken language, but it shows purity of heart and competence instead.
I'll take the latter.

s/competence/incopetence/


--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

p = (struct foo *) malloc(sizeof(struct foo));

In the latter case p might still have the wrong type, but at least
you can tell at a glance that the compiler will complain if so.

No, it will not.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

Richard Tobin a écrit :

C++ NEEDS the cast... If you ever want to compile your code in C++ mode
it is better to leave the cast there.

Naiva spots crap once again.

Even when you really have no other chance in C++ to use malloc you
must avoid casting in C - even as in C++ you can't - but for that you
must learn C++ and how to write C in C++ syntactically right.

But you proves once again that your know ledge of both C and C++ is
not good enough to gget one of both right.

Yes, we know that you spams constantly for a product that you have the
sources under control - but you've not written but adoped.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

I've been trying to switch to the habit of adding the cast, but I find that
I keep forgetting!

Why? malloc returns a pointer to void - and that is a generic pointer
compatibel to any pointer to object. No cast needed.

There is a rule you should learn: never ever cast except you KNOWs
exactly WHY you must cast. However when you knows not exactly why but
you means you must cast then use a good book, the clc FAQ, or this
group to get more information. Be sure most when not always you'll end
up with 'no cast needed'.
So, by itself, this rule has less worth than a rule which says ``it is poor
practice to make some kind of mistake in your use of malloc'', because
it's essentially a rule which says ``it is poor practice to do something that
is often correct, and even considered a good idiom by some C programmers,
some of whom are experts, and for the violation of which there is no diagnostic
support in the standard language''.

Uh, oh, there is enough diagnostic support - but it is on you to set
the warning level right. Yes, sometimes the compiler will give you a
diagnostic you'll not accept - but that's your fault. It may look like
an orakel but there is no rule that requires that the compiler should
NOT play the role of an orakel.
But what /is/ poor practice is designing a programming language feature which
allows you to request dangerous conversions without having to provide a written
token that marks the places in the program where these conversions
are happening. You simply use the assignment operator between two expressions,
the same way like you could do in a typeless language between /any/ two
expressions. (E.g. a predecessor of C, like BCPL).

In question you would use lint until it says that the source is
errorfree before you start to compile. lint is known as the
errorchecker, the compiler has only to compile and will whine only
when it falls in holes.
The void and void * types come from C++ which has a better rules for them.
It doesn't have the implicit conversion from void * to any object type,
and C++ doesn't have the silly (void) hack. (Someone should have had the
balls to uproot non-prototype declartions from C back in in 1989: boldly
removing something from a standard doesn't mean compilers can't continue to
support it).

Oh, void* is not compatible to any object type! It is only compatible
to any pointer type, except function pointer. And yes, it is much
better than the old plain char* as it explicity declarees that it is
not simply a char* but a pointer with unknow type.
C once had malloc which returned char *. There was nothing wrong with this; it
did not have to change. If anywhere at all, the void * type should have only
been used in the free and realloc functions for passing pointers down.

Oh, there is a big difference between char* and void*. A void* is
mostenly not simple a char* and a char* is not a void* even as the
converation can be done without cast.
So, the thing to do is to eliminate the use of the void * type from your C
programs, at least in situations where it will likely be converted to another
type.

In contrary, learn how to use void* and what makes the difference to
char*.
I recommend that interfaces which accept a generic pointer-to-anything continue
to be written to accept void * arguments, but when those pointers are returned,
they should be returned as unsigned char *.

No, because char* is not identical with void*, they are different
types with different meaning even as they may internal binary
identical.

void* = a pointer pointing to an object of unknown type and size.
char* = a pointer to either an array of chars or a single char.

by that: there are 3 different char*. The old days without void* are
gone - and that is good!
Rationale: it is correct and sometimes useful to be able to treat any
object as an array of bytes (unsigned char *). If we don't know what a
pointer points to, but we do know that it points to an object, then
it's okay for that pointer to be a pointer to unsigned char *. We can
safely dereference that pointer to access a byte. Functions that call
malloc only to obtain a block of bytes should not have to do the cast
since any object already /is/ a block of bytes.

No. char* is either a pointer to a single char or an array of chars or
a string. C does not distinguish between the 3 different object type.
If the object is to be used as some other type, even as a char * string, then a
cast will be required. So, you don't need your rule 10: just design the API so
that code won't compile without the cast.

No, that is the charm of C. void* is compatible to any other pointer
type without conversation - and that is fine. Use void* when the type
of the object pointing to doesn't matter, use char* when the object
the pointer points to is either a single char, an array of chars or a
string. Use another type when the pointer points to another type.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Herbert Rosenau

Aside from the missing-prototype issue a type for the return from the
allocation function. Something like:

#define CALLOC_TYPE(type) ((type*)calloc(1, sizeof(type)))
#define CALLOC_ARRAY(count, type) ((type*)calloc(count, sizeof(type)))

(and probably malloc versions as well).

Those would probably take care of most actual allocations, and help
prevent type errors. And of course the raw functions remain
available, if those better serve the purpose.

Nonesense! The casts are superflous and errornouse! Instead to avoid
an error you are creating one - hiding an error by cast is not the
right way to remove it. You does in no way prevent a type error - but
generates one instad by casting the return value.

And calloc is only in single case the right function: you'll allocate
a char array. In any other case you'll create an error, because
setting another tye as char bytewise to 0 will produce unexpected
results when the implementation uses padding bits for values other
than bytes. That makes calloc still useless.

By that: most halfways modern implementations will still overwrite the
allocated block to overwrite content not written by the application
calling malloc for security. That makes calloc superflous and
errornous too.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
S

spinoza1111

That is a rich source of humor.

Yeah, the guys at the office are really funny. Posting this type of
material (and your Vitriolic Tirade) is the twenty-first century's
equivalent of Xeroxing your ass. I've decided to ignore the thread,
but I'm sure we'd all love to hear your opinions about the book in the
form of another tirade.

You seem to specialize in making a fool of yourself, and finding
fellow fools as well.
 
S

spinoza1111

The term "lazy evaluation" already has a well-established meaning in
computer science, and that isn't it.  It refers to deferring the
evaluation of an expression until its value is needed.  One of its
interesting uses is that it allows you to build infinite data
structures, part of which you would presumably eventually evaluate
to (for example) print it out.

When a field is overrun by people who move their lips when they read,
and have learned by rote, nothing has to do with anything, and
"terminology" trumps knowledge. Short-circuit evaluation is a type of
lazy evaluation. It is true that I might have used a different word
today given the increasing popularity of forms of lazy evaluation that
are not short circuit evaluation, but my use of the term was clearly
defined.

And do note that in criticising a use of language, your own language
uses what could by the same use of hermeneutic to come up with a
smartassed remark, be similarly criticised: for it's lazy indeed to
say "print it out".

Computer language seems precise but it's filled with fantasy, metaphor
and terms of art. In talking to a Haskell specialist with a tin ear,
"lazy evaluation" causes resentment. I'll make a note of it.
 
B

Ben Bacarisse

Herbert Rosenau said:
Oh, void* is not compatible to any object type! It is only compatible
to any pointer type, except function pointer.

I think you are miss-using the term "compatible". The definition of
compatible types is very strict. void * is compatible only with
itself.

<snip>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,008
Messages
2,570,268
Members
46,867
Latest member
Lonny Petersen

Latest Threads

Top