Are “malloc(sizeof(struct a *))” and “malloc(sizeof(struct a))” the same?

There seems to be a fundamental misunderstanding.

malloc(sizeof(struct a) * n) allocates n number of type struct a elements.

No, that’s just what one usually does use it as after such a call. malloc(size) allocates a memory region of size bytes. What you do with that region is entirely up to you. The only thing that matters is that you don’t overstep the limits of the allocated memory. Assuming 4 byte float and int and 8 byte double, after a successful malloc(100*sizeof(float));, you can use the first 120 of the 400 bytes as an array of 15 doubles, the next 120 as an array of 30 floats, then place an array of 20 chars right behind that and fill up the remaining 140 bytes with 35 ints if you wish. That’s perfectly harmless defined behaviour.

malloc returns a void*, which can be implicitly cast to a pointer of any type, so

some_type **array = malloc(100 * sizeof(data *)); // intentionally unrelated types

is perfectly fine, it might just not be the amount of memory you wanted. In this case it very likely is, because pointers tend to have the same size regardless of what they’re pointing to.

More likely to give you the wrong amount of memory is

data *array = malloc(n * sizeof(data*));

as you had it. If you use the allocated piece of memory as an array of n elements of type data, there are three possibilities

  1. sizeof(data) < sizeof(data*). Then your only problem is that you’re wasting some space.
  2. sizeof(data) == sizeof(data*). Everything’s fine, no space wasted, as if you had no typo at all.
  3. sizeof(data) > sizeof(data*). Then you’ll access memory you shouldn’t have accessed when touching later array elements, which is undefined behaviour. Depending on various things, that could consistently work as if your code was correct, immediately crash with a segfault or anything in between (technically it could behave in a manner that cannot meaningfully be placed between those two, but that would be unusual).

If you intentionally do that, knowing point 1. or 2. applies, it’s bad practice, but not an error. If you do it unintentionally, it is an error regardless of which point applies, harmless but hard to find while 1. or 2. applies, harmful but normally easier to detect in case of 3.

In your examples. data was 4 resp. 8 bytes (probably), which on a 64-bit system puts them into 1. resp. 2. with high probability, on a 32-bit system into 2 resp. 3.

The recommended way to avoid such errors is to

type *pointer = malloc(num_elems * sizeof(*pointer));

Leave a Comment