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
?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With