Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Comments on How to properly use malloc?
Parent
How to properly use malloc?
I have seen this construct quite a lot:
int *p = (int*) malloc(n * sizeof(int));
Is this how it should be? What is the proper way to use malloc
? I have also seen this:
int *p = malloc(n * sizeof *p);
Which one is preferable?
Post
Should we cast the result of malloc?
The cast to the intended type is not necessary in C, since during assignment, the void*
returned by malloc
can be implicitly converted to any other object pointer and vice versa (as per C17 6.5.16.1). So the cast does add clutter. However, there's nothing wrong with being explicit by types either, so it is mostly a matter of coding style.
There are some historical and compatibility reasons why you would/shouldn't cast:
- C++ doesn't allow implicit conversions with void pointers so the cast is necessary if C++ compatibility is desired. (However, using
malloc
in C++ is very questionable and dangerous, but that's another story.) - The old C90 standard supported dangerous implicit int default function declarations, so if you forgot to
#include <stdlib.h>
then added a cast to themalloc
result, you would hide the bug that the missing include introduced.
How should we write the size parameter to malloc?
There's several possible options here:
malloc( n * sizeof(int) )
malloc( sizeof(int[n]) )
malloc( n * sizeof *p )
calloc( n, sizeof(int) )
calloc( n, sizeof *p )
-
malloc( n * sizeof(int) )
has the advantage of being clear and explicit, this would be the most "old scool" way of usingmalloc
and probably the most common method taught in C books.The argument against this style is that in case we change the type of
p
, then themalloc
call turns incorrect. I always thought that this was a rather weak argument - because: yes if you only change part of a code without considering all of it, it will break. That goes for all C code. -
malloc( sizeof(int[n]) )
is very similar to the above one, except it is slightly more readable. It uses a VLA declaration though, so it's not 100% portable to old or exotic C implementations. -
malloc( n * sizeof *p )
uses a trick, namely that thesizeof
operand isn't evaluated for side-effects here, so we can actually de-reference a pointer to get the size of the pointed-at type, without the code actually executing that de-referencing (which would have been wildly undefined behavior).The argument in favour of this style is the opposite of the argument against the above ones - in case
p
changes type then themalloc
expression is still valid. That's nice. Arguments against this style will follow further below. -
The
calloc
versions are 100% equivalent to theirmalloc
counterparts, except it will also zero-out all memory. Meaningcalloc
is slower but it provides extra functionality.
Why sizeof *p
isn't some universally great style
The argument for using the *p
style doesn't scale well when we start dealing with more complex situations like pointer-to-pointers or 2D arrays. Consider this:
void failloc (int n, int p[5][5])
{
p = malloc(n * sizeof *p);
printf("Yay we allocated %zu bytes\n", n * sizeof *p);
}
When calling this code as failloc(1, arr)
then we expect 1*5*5*sizeof(int)
bytes = 100 bytes on 32 bit computer. However, it only allocates 1 * sizeof(int[5])
. That is, 20 bytes, which is of course wrong.
One may then argue and say hey, you picked the wrong type, it should have been int (*p)[5][5]
and the code will work. Yes indeed, if I change the type of p
it will start working. Now, what was the initial argument for using this style again?
In case an int**
was passed to a similar function, then again the style doesn't scale well, consider this (bad!) code:
int** failloc (int x, int y,)
{
int** p = malloc(x * sizeof *p);
for(int i=0; i<y; i++)
p[i] = malloc(y * sizeof **p);
return p;
}
We have to do **p
suddenly which is inconsistent. Now what if I change the type of p
to int*
? Hey, you can't go change the type of p
again, stop doing that!
Yet another scenario: flexible array members.
typedef struct
{
int foo;
int bar[];
} flexy;
flexy* p = malloc(sizeof *p + n * sizeof /* what? */ );
We can't write sizeof *bar
, there's no such identifier. We have to write sizeof *p->bar
. This simply isn't readable.
The only sensible way out is to combine the *p
style with one of the other two. Personally I'd probably have written that line as
malloc( sizeof *p + sizeof(int[n]) );
Or for those who insist on using the same style consistently:
malloc(sizeof(flexy) + n * sizeof(int) );
So overall I don't think the sizeof *p
trick is nearly as smart as people like to claim, it's nice in some situations, it doesn't work in other situations.
Overall the programmer must actually know what they are doing - no way around it. If you change the type of some variable, then you better review every single line in the code base using that variable - simple as that.
I don't think any of these styles come with any significant benefits or strong arguments for using one over the other - it is all very subjective. There is no obvious right or wrong style here.
0 comment threads