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
isstruct n**
aka a pointer to another pointer.But shouldn't this declaration work in theory because:
malloc
returns typestruct n*
(a pointer)- and
p
points to the pointer thatmalloc
returns - so it is essentially a pointer to another pointer
- 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;…
- and
p
points to the pointer thatmalloc
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:
- The compiler automatically casts void * to struct n **
- 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...