Using sizeof() with pointer to struct in C

Viewed 581

I'm learning C right now and came to a little problem I encountered while trying out some code snippets from my uni course.

It's about typedef'd pointers to structs and their usage in the sizeof() function.

#include <stdio.h>
#include <stdlib.h>

// Define struct
struct IntArrayStruct
{
  int length;
  int *array;
};
// Set typedef for pointer to struct
typedef struct IntArrayStruct *IntArrayRef;

// Declare function makeArray()
IntArrayRef makeArray(int length);

// Main function
int main(void)
{
  // Use makeArray() to create a new array
  int arraySize = 30;
  IntArrayRef newArray = makeArray(arraySize);
}

// Define makeArray() with function body
IntArrayRef makeArray(int length)
{
  IntArrayRef newArray = malloc(sizeof(*IntArrayRef)); // ERROR
  newArray->length = length;
  newArray->array = malloc(length * sizeof(int));
  return newArray;
}

And the code really works in the IDE used in class (Virtual C), but when I try the exact same example in VSCode and compile it using GNU Make or GCC, it returns an error because sizeof(*IntArrayRef) in the malloc() function call is marked as an unexpected type name.

error: unexpected type name 'IntArrayRef': expected expression

However, when I change it to sizeof(IntArrayStruct), everything works splendidly.

Isn't *IntArrayRef the same value as IntArrayStruct?

3 Answers

IntArrayRef is the name of a type, therefore *IntArrayRef is invalid syntax. What you can (and should) do instead is give the name of the variable and dereference that:

IntArrayRef newArray = malloc(sizeof(*newArray)); 

Here's how your type names relate to each other:

struct IntArrayStruct *  == IntArrayRef

Thus, newArray has type IntArrayRef which is the same as struct IntArrayStruct *

So, if you want the size of the pointer type, you'd use one of

sizeof (IntArrayRef)
sizeof (struct IntArrayStruct *)
sizeof newArray

If you want the size of the pointed-to type (the actual struct type), you'd use one of

sizeof (struct IntArrayStruct)
sizeof *newArray

sizeof is an operator, not a function - parentheses are only required if the operand is a type name (including typedef names). It doesn't hurt to use parentheses around non-type operands like sizeof (*newArray), but they're not necessary.

As a stylistic note, it's generally a bad idea to hide pointer types behind typedefs, especially if the user of the type has to know it's a pointer type to use it correctly. IOW, if the user of the type ever has to dereference something, then the pointerness of that something should be explicit. Even if the user doesn't need ever need to explicitly dereference it, you still shouldn't hide the pointerness of the type (take the FILE * type in the standard library as an example - you never actually dereference a FILE * object, but its pointerness is still made explicit).

Otherwise, be prepared to write a full API that hides all pointer operations from the user.

Compare

sizeof(IntArrayStruct*)
sizeof(IntArrayRef)

vs

sizeof(IntArrayStruct)

The first two are the same, and they are the size of just the pointer. I.e. same as sizeof(int*), sizeof(long*), sizeof(void*) etc.

The third is the size of the actual data structure. That's what you want if you are creating space for it with malloc.


Also Pointers and References are two different things in C++ , so it might be less confusing to communicate the fact that something is a pointers, with the abbreviation "ptr".

Finally, as mentioned, the creating a new type name, just to represent a pointer to a struct type, is non-standard. It would confuse other people without much benefit.

Related