4

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?

lbeul
  • 55
  • 4
  • 3
    Use `sizeof(*newArray)`. – Barmar Dec 17 '20 at 17:51
  • 4
    BTW, it's generally considered poor style to typedef pointers. See https://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers – Barmar Dec 17 '20 at 17:52
  • `sizeof` is not a function, it is an operator. Instead of `sizeof(*newArray)`, use `sizeof *newArray` – William Pursell Dec 17 '20 at 17:55
  • `sizeof(IntArrayStruct)` would not be valid in C because `IntArrayStruct` is a tag, not a type and is meaningless unless it is preceded by `struct`, `union` or `enum`, or there is a variable or a typedef with that name. (Perhaps you compiled it as C++?). – Ian Abbott Dec 17 '20 at 17:59
  • @WilliamPursell So what's the result of `sizeof *newArray * 4`? Is is the same as `sizeof ( *newArray * 4 )` or `( sizeof *newArray ) * 4` **Will the newly-hired developer responsible for maintaining the code three years from now know?** `sizeof( *newArray )` is ***clear***. `sizeof *newArray` is not. – Andrew Henle Dec 17 '20 at 18:01
  • @Andrew Henle Operator precedence: **sizeof** has a higher precedence than multiplication, therefore the right answer would be (sizeof *newArray) * 4, otherwise sizeof (*newArray * 4). **sizeof** is well documented, the newly-hired developer 'should' know that. – Erdal Küçük Dec 17 '20 at 18:11
  • @AnttiHaapala OP said it works with MSVC, just not with gcc. Or do you mean it's not working for you even with MSVC? – Dmitri Dec 17 '20 at 18:14
  • @Dmitri I tested every single MSVC version on Godbolt's Compiler Explorer with both C **and** C++, and each and every single one of them fails on that line with the exact same error message: (27): error C2275: 'IntArrayRef': illegal use of this type as an expression – Antti Haapala -- Слава Україні Dec 17 '20 at 18:17
  • @Andrew Henle I agree with you. I hereby take my second statement back. – Erdal Küçük Dec 17 '20 at 18:29
  • @ErdalKüçük I learned a lot when I had a few inexperienced developers working in my team - they were smart, but they made mistakes that more experienced developers wouldn't. One thing I learned was it's better to use parentheses whenever there's a question about their use, and especially when they make the code clearer. In short, I learned to write code in ways that avoid bugs. C is bug-prone enough. Mainly because I was usually the one getting called in when something broke... – Andrew Henle Dec 17 '20 at 18:35
  • @AndrewHenle If you are concerned about precedence, you can write `4 * sizeof *newArray` or `(sizeof *newArray) * 4`. Will the newly hired developer mistakenly think that `sizeof *newArray` is a function call? Absolutely not. Will that inexperienced developer mistakenly think that `sizeof(*newArray)` is a function call? Maybe. `sizeof *newArray` is clear. `sizeof(*newArray)` is not. – William Pursell Dec 17 '20 at 19:05
  • @AnttiHaapala Sorry for the typo and the late answer. The IDE used in class is not MSVC, but a thing called **Virtual-C** - an IDE for education purposes only, showing memory diagrams during runtime and so on. I talked it through with my professor and he approved that this IDE has its quirks & bugs and was mainly chosen because it's free and lightweight. Anyway, thanks a lot for your help! – lbeul Oct 28 '21 at 08:47

3 Answers3

3

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)); 
dbush
  • 205,898
  • 23
  • 218
  • 273
2

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.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Thanks for this insightful answer! I changed it to `sizeof(*newArray)`, as it's exactly what I wanted. However, I'm still wondering - why does this other IDE, VisualC, work with `sizeof(*IntArrayRef)` if it should be nothing but a syntax error? – lbeul Dec 18 '20 at 10:03
  • @lbeul: Good question. I don't know the answer. MSVC has always had some issues, but I'm honestly surprised that code compiled. – John Bode Dec 18 '20 at 15:18
0

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.

Pete W
  • 116
  • 2