Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

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?

+10
−0

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?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

Post
+9
−0

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 the malloc 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:

  1. malloc( n * sizeof(int) )
  2. malloc( sizeof(int[n]) )
  3. malloc( n * sizeof *p )
  4. calloc( n, sizeof(int) )
  5. 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 using malloc 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 the malloc 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 the sizeof 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 the malloc expression is still valid. That's nice. Arguments against this style will follow further below.

  • The calloc versions are 100% equivalent to their malloc counterparts, except it will also zero-out all memory. Meaning calloc 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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

2 comment threads

sizeof(*p) (1 comment)
I would argue that `void failloc (int n, int p[5][5])` isn't a very good counterexample. Using arrays... (3 comments)
I would argue that `void failloc (int n, int p[5][5])` isn't a very good counterexample. Using arrays...
klutt‭ wrote almost 3 years ago

I would argue that void failloc (int n, int p[5][5]) isn't a very good counterexample. Using arrays as parameters can indeed be very confusing.

And regarding your pointer to pointer example, I'd say that it is very consistent. You just do p[i] = malloc(y * sizeof *p[i])

Lundin‭ wrote almost 3 years ago

klutt‭ The equivalent int (*p)[5] would have been just as problematic. There's plenty of examples where the style simply doesn't work well.

klutt‭ wrote almost 3 years ago · edited almost 3 years ago

Lundin‭ What's problematic with that one? I do indeed expect sizeof *p to be 20 bytes with 32-bit int, which it also is. (I checked because you made me believe it would be something else :) )

I'd also like to add that it's a pretty unrealistic example. You declare a local pointer in the argument list and then immediately use it to allocate memory. Why not declare it in the function body then?