1

I am new to C and have a question about a specific pointer declaration:

Here is a program i wrote:

#include <stdlib.h> struct n { int x; int y; }; int main() { struct n **p = malloc(sizeof(struct n)); return 0; } 

The declaration here is not be correct but why not?

Here is my thought process:

  • The man page of malloc specifies that it returns a pointer:
The malloc() and calloc() functions return a pointer to the allocated memory, which is suitably aligned for any built-in type. 
  • The type of p is struct n** aka a pointer to another pointer.

  • But shouldn't this declaration work in theory because:

  1. malloc returns type struct n* (a pointer)
  2. and p points to the pointer that malloc returns
  3. so it is essentially a pointer to another pointer
  4. so the type of p is fulfilled

Sorry if this is a dumb question but i am genuinely confused about why this does not work. Thanks for any help in advance.

1
  • 1
    It will compile as written, but try to figure out how to add more code to use the double ptr. The wheels will come off the cart real quick... Commented Aug 25, 2022 at 20:50

3 Answers 3

6

The return type of malloc is not struct n *, regardless of how it is called. The return type of malloc is void *.

Initializing a struct n ** object with a value of type void * implicitly converts it to struct n **. This implicit conversion is allowed, because the rules for initialization follow the rules for assignment in C 2018 6.5.16.1 1, which state one of the allowed assignments is:

… the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;…

  1. and p points to the pointer that malloc returns

No, the value p is initialized to the value that malloc returns. Then p points to the memory malloc allocated.

It is a mistake for this code to allocate enough space for a struct n (using malloc(sizeof(struct n))) but assign the address of that space to the struct n ** that is p. To point to a struct n, use struct n *p = malloc(sizeof (struct n)); or, preferably, struct n *p = malloc(sizeof *p);.

To pointer to a pointer to a struct n, first create some pointer to a struct n, as with the above struct n *p = malloc(sizeof *p);. Then a pointer to that pointer would be struct n **pp = &p;.

If you wanted to allocate space for those pointers-to-pointers, you could do it with struct n **pp = malloc(sizeof *pp);, after which you could fill in the pointer to the struct n with *pp = malloc(sizeof **pp);. However, you should not add this additional layer of allocation without good reason.

Note that the form MyPointer = malloc(sizeof *MyPointer); is often preferred to MyPointer = malloc(sizeof (SomeType)); because the former automatically uses the type that MyPointer points to. The latter is prone to errors, such as somebody misintepreting the type of MyPointer and not setting SomeType correctly or somebody later changing the declaration of MyPointer but omitting to make the corresponding change to SomeType in the malloc call.

Sign up to request clarification or add additional context in comments.

2 Comments

This is a great answer thank you so much for taking the time to explain this, i greatly appreciate it. I have one last question: In your answer you mentioned using malloc(sizeof(*MyPointer)), how does this exactly work? i can see that you dereference MyPointer but wouldnt that just lead to undefined behavior since it is in the middle of being assigned and potentially points to nothing? I am quite confused on how that would work.
@programme3219873: An expression operand of sizeof is not evaluated. The compiler only uses it to analyze its type.
1

This doesn't really work. In this example, malloc is returning a void *, which points to a newly allocated place in the heap that is large enough to hold a struct n.

Note that malloc() returns type void *, which is basically a pointer to any potential type, malloc() DOES NOT return type struct n * (a pointer to your declared struct type). void * has the special property of being able to be cast to any other type of pointer, and back again.

All together this means that your code doesn't actually work, since the first dereference of your struct n** would be a struct n, not a struct n*. If you tried to dereference twice, you would most likely get an invalid memory reference and crash.

The reason that your code can compile and run without crashing is because:

  1. The compiler automatically casts void * to struct n **
  2. You never attempt to actually dereference your pointer, which means you never attempt an invalid memory reference.

3 Comments

"void * has the special property of being able to be cast to any other object type of pointer: " void * to/from a function pointer has issues.
@chux-ReinstateMonica I didn't realize that, I've honestly rarely used function pointers in C, but I suppose that makes sense since functions in C are not first class values. What kind of issues exactly?
Example: a function pointer might be 128-bit and void * 64-bit. Saving such a function pointer into void * loses non-recoverable information if a conversion back to a function pointer is attempted. C lacks a truly universal pointer.
1

Simplify the problem and understanding may come:

int main() { struct n data; struct n *pData = &data; // skipped in your version struct n **ppData = &pData; // struct n **ppData = &data; // Will not compile // struct n **ppData = (struct n **)&data; // Casting but wrong! return 0; } 

Because malloc() returns a void ptr, you are free to store the pointer into anything that is a pointer datatype. On your head if you put it into the wrong datatype...

3 Comments

Re “Because malloc() returns a void ptr, you are free to store the pointer into anything that is a pointer datatype”: void (*f)() = malloc(1); violates a constraint and will generate a diagnostic.
@EricPostpischil You got me there. But, that function returns void (if that can be conceived.). It is not, itself, a void datatype. Apples and oranges, don't you think?
@Fe2O3: The result of malloc is not being stored into a function. That initialization asks to store it into a pointer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.