Malloc strangeness

I've been focusing on C lately, coming from languages based on C has helped alot. Now, C is at times surprisingly simple and yet sometimes surprisingly devilish. So far I've learned that attention to the finer works of C can help improve my understanding.

I am experimenting on a FreeBSD 9.1 environment

However, I have come across a field where the reasoning makes sense, but practical examples and tests seems to throw me for a loop, and I don't really know where to go for help.

The culprit? Malloc.

Everywhere I read, malloc should allocate the amount of memory you need for a particular need, like house a char *var or a struct or the like.

As such

PHP Code:

myStructPtr = (myStruct *)malloc(sizeof(myStruct));

Makes good sense. There seems to be a debate as to whether the (myStruct *) explicit cast is nice C or bad C.

Now, here's where things start to get to me.
Consider the following utterly contrieved example.

Look at the mallocs. I deliberately allocate less-than-required memory for the a, b, c and d pointers before filling them with the contents of the ptr pointer. The e pointer is malloc'd as I've learned to do.

As for b, which essentially is malloc(0), it seems to be implementation dependant and can even be undefined behaviour.

When this code is compiled and run on my playground, the strings a and b looks very weird while c, d and e looks normally.

> Makes good sense. There seems to be a debate as to whether the (myStruct *) explicit cast is nice C or bad C.
Not really - those that know C well enough know it to be largely a bad thing.

It has no positive benefits, and some very bad negative effects.
The most serious is that with a cast, you DON'T get a warning if you fail to include stdlib.h. This leaves malloc without a prototype, and implicitly declares int malloc();
Yes, malloc now returns an int (apparently). On machines with different sized pointers and ints (actually pretty common on 64-bit machines), this trashes pointers in unusual ways (but the cast masks all this).

If you write it as

Code:

myStructPtr = malloc(sizeof(*myStructPtr));

Then a whole host of maintenance problems go away.

> The e pointer is malloc'd as I've learned to do.
And even that is wrong.
You should have allocated strlen(ptr)+1 bytes, because you need to allow room for the \0 at the end of the string.

> - Why don't we get a core dump when we allocate less than required storage and plunge data inside?
> - Why the strange output when printing a and b?
> - Why does c and d behave as if nothing is wrong?
All of these can be summarised as pure dumb luck.
If you screw up, there are no guaranteed diagnostics. Anything can happen, including (apparently) giving you the right answer.

For example, your failure to allocate strlen(ptr)+1 would be quite likely to go unnoticed for some length of time before it was caught.

Which is why serious s/w development uses tools like valgrind to help locate memory issues which don't show up through regular testing.

Your code would almost certainly crash if you tried further operations such as more malloc calls, or calling free after all those memory corrupting strcpy calls.

One of the best ways to learn with self studies are to have things blow up in your face. It's much harder to learn which you do well (or bad!) when they SEEM to function as you want them to.

I still make a gazillion errors and following corrections or reading man-pages or googling while developing or peaking into the FreeBSD source to try to decipher how some property was obtained. However, in this specific case with malloc, the malloc source is pretty daunting for inexperienced C programmers.

One of the things I have to improve a lot is to use the compiler (gcc in my case) more aggressively towards my code.

I have tried lint, but it reports things anywhere, even in the C header files, so I'm not really sure what to make of it. I'm installing valgrind as we speak to, well, just play around and see where it takes me.

I have so far used the constructive feedbacks to go over some of my other code to improve some of the areas, specifically typecasting malloc.