1

Today,I noticed a bunch of code that Instantiate a struct like below while ServiceMsg is a struct.

ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);

I have never seen malloc can be used like this. Can anyone explain this to me??? size of buffer is often assigned like this malloc(sizeof(int) * n) as is known to me..

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
Caiyi Zhou
  • 143
  • 1
  • 9
  • 4
    That's `sizeof(*msg)`, not a multiplication. (And `new msg` would be a million times better (And a smart pointer another million times better again (And there's no language called `C/C++`))) – Ken Y-N Nov 13 '20 at 07:10
  • @john Why is that? – Sourav Ghosh Nov 13 '20 at 07:13
  • @KenY-N You know I think I would have spotted that had it not been for the space between the `*` and `msg`. – john Nov 13 '20 at 07:13
  • To make it clear to the OP. Parens are only required in `sizeof(X)` if `X` is a type. If `X` is an expression (as above) they are not. – john Nov 13 '20 at 07:16

4 Answers4

3

In the snippet,

  ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);

is the same as

 ServiceMsg *msg = (ServiceMsg *)malloc( 1* sizeof(*msg));

which is better written as

 ServiceMsg *msg = malloc(sizeof(*msg));

To elaborate, malloc() expects a size as the argument, and in this call, the size we are passing is sizeof(*msg), i.e., sizeof (ServiceMsg ).

That said, please see this discussion on why not to cast the return value of malloc() and family in C..

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
3

There's a few details that might make it confusing:

  • The sizeof operator can be used in two ways: either with parenthesis sizeof(type), but then the argument must be a type like int. Or without parenthesis sizeof expr, then the argument can be an expression. But of course an expression can contain a parenthesis in itself...

  • In your case sizeof *msg takes the size of the expression *msg, which means de-reference the pointer msg (it's not the multiplication operator!) so we end up with a "lvalue" temporary variable of type ServiceMsg. Which has the size of a ServiceMsg.

  • Normally, de-referencing a pointer variable at the same line it is declared would be a big no-no, it is not initialized and is not yet pointing at any sensible location. The catch is that the sizeof operator does not actually evaluate (execute) its operand, so the expression *msg is never executed and de-referencing never happens.

This is why the code works. This style is often even recommended, since you don't have to worry about what type the pointer is pointing at - it's always type* ptr = malloc(sizeof *ptr); regardless of what you are allocating.

The cast to (ServiceMsg*) in your code is superfluous.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for explanation!! but what does this mean: type ptr* = malloc(sizeof *ptr) ?? I change type to int and tried it in my machine,It blocks on compiling. – Caiyi Zhou Nov 16 '20 at 06:17
  • @CaiyiZhou It was a typo, fixed. But besides that I meant that instead of `type` you can write any valid type there and it works the same. – Lundin Nov 16 '20 at 07:11
0

It would be more clear if to rewrite this call where the expression looks like a multiplication expression

malloc(sizeof * msg);

like

malloc(sizeof *msg);

The sizeof operator is defined the following way

sizeof unary-expression
sizeof ( type-name )

The expression *msg that is dereferencing the pointer msg is an unary expression. The type of the expression is determined (in this case it is ServiceMsg) and the size of an object of this type is calculated. The expression itself is not evaluated. The compiler only determines its type.

You could this unary expression enclose in parentheses getting a primary expression like

malloc(sizeof ( *msg ) );

In this this record looks similarly like this record

malloc(sizeof ( ServiceMsg ) );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

Cleaning that up a little bit, we get

ServiceMsg *msg = malloc(sizeof *msg);

which is exactly the same as

ServiceMsg *msg = malloc(N * sizeof *msg);

where N == 1. This is a fairly common idiom in C for dynamically allocating memory:

T *p = malloc( N * sizeof *p ); // for any non-function type T

or

T *p;
...
p = malloc( N * sizeof *p );

or

T *p = calloc( N, sizeof *p );

or

T *p;
...
p = calloc( N, sizeof *p );

It has the advantage of not repeating type information, so maintenance is easier.

The cast is unnecessary1, and since the type of the expression *msg is ServiceMessage, sizeof *msg == sizeof( ServiceMessage ).

Remember that sizeof is an operator, not a function - parentheses are only required if the operand is a type name like ServiceMessage or int or char, or if it's an arithmetic expression like 1 + 2. Since sizeof is a unary operator it has higher precedence than an arithmetic operator, so sizeof 1 + 2 is parsed as (sizeof 1) + 2, which probably isn't what you want.


  1. Unless you're compiling this code as C++ or using an ancient K&R implementation. Prior to C89, the *alloc functions returned char * instead of void *, so a cast was necessary if the target wasn't a char *. C++ does not allow implicit conversion from void * to other pointer types, so it requires a cast as well, but if you're writing C++, you shouldn't be using C-style memory management anyway.
John Bode
  • 119,563
  • 19
  • 122
  • 198