Home > Blockchain >  Why does this pointer declaration work with malloc()?
Why does this pointer declaration work with malloc()?

Time:08-26

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.

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

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...

  • Related