What happens when you dereference a pointer to a struct

"I am currently a Software Engineering student at ALX. I'm passionate about technology and enjoy conducting research to find answers on my own. I have a natural inclination to ask 'WHY' more often than 'HOW'.
"While working on projects at ALX, I have acquired a wealth of interesting and diverse knowledge about software engineering and computer science in general. Therefore, I needed a place to store and save all this information, allowing me to refer back to it whenever I forget."
Emphasis on the example below:
listint_t is a struct
listint_t *node;
node = malloc(sizeof(*node));
Now, let's consider the following line from the code:
listint_t *node;
This line declares a pointer variable named node of type listint_t*, which means it is a pointer to a listint_t struct.
When we dereference node using the * operator (*node), we get the actual object it points to. In this case, node points to a listint_t struct, so *node represents an instance of the listint_t struct.
Therefore, when we use sizeof(*node), it calculates the size of the listint_t struct, which is the object type that node points to.
Accessing elements in a struct
I used to think just using '*' is enough to access a struct, but using '*' only accesses the object itself, you to need to use dot notation or arrow operator to access the elements.
typedef struct listint_s
{
const int n;
struct listint_s *prev;
struct listint_s *next;
} listint_t;
To create an instance of the listint_t struct, you need to allocate memory for it using malloc or a similar memory allocation function. The line listint_t *node; declares a pointer variable named node of type listint_t*, which can hold the memory address of a listint_t struct.
To access the members of the listint_t struct through the node pointer, you need to dereference it using the * operator. The expression *node dereferences the pointer and gives you the actual object it points to, which is an instance of the listint_t struct.
Here's an example to illustrate the concept:
Using dot notation(member access)
listint_t *node = malloc(sizeof(listint_t)); // Allocate memory for a listint_t struct
(*node).n = 42; // Access and assign a value to the n member
(*node).prev = NULL; // Access and assign a value to the prev member
(*node).next = NULL; // Access and assign a value to the next member
Alternatively, you can use the arrow operator (->) as a shorthand notation to access the members of a struct through a pointer. The arrow operator combines the dereference and member access operations.
Here's an equivalent code snippet using the arrow operator:
listint_t *node = malloc(sizeof(listint_t)); // Allocate memory for a listint_t struct
node->n = 42; // Access and assign a value to the n member
node->prev = NULL; // Access and assign a value to the prev member
node->next = NULL; // Access and assign a value to the next member
To summarize, dereferencing the node pointer using *node gives you the actual object it points to, which is an instance of the listint_t struct. You can then access the members of the struct using either the dot operator (.) or the arrow operator (->).
sizeof(node)
If you modify the code to allocate memory without dereferencing the node pointer, like this: node = malloc(sizeof(node)), it would not work as expected.
In this case, sizeof(node) would give you the size of the pointer node itself, not the size of the listint_t struct. It would allocate memory only for the size of the pointer, which is typically 4 or 8 bytes (depending on the architecture). This would lead to incorrect memory allocation and potential undefined behavior when accessing the struct members.
To allocate memory correctly for a listint_t struct, you should use sizeof(listint_t) or sizeof(*node) in the malloc function. Here's the correct allocation:
node = malloc(sizeof(listint_t));
or
node = malloc(sizeof(*node));
Both expressions ensure that you allocate memory for the size of the listint_t struct, which is the appropriate size required to store the members (n, prev, next) of the struct.
Always make sure to use the correct size when allocating memory to ensure proper memory allocation and avoid potential issues with accessing the struct members.



