C - Pointers and prepending in a Singly Linked List - c

I am trying to understand the pointer logic behind prepending.
I declared a struct as follows:
typedef struct myList{
int info;
struct myList *link; //self referential structure;
} Node;
For memory allocation in the heap memory segment, I use the following function:
Node *getNode(){
return ((Node *)malloc(sizeof(Node)));
}
In the main function, I allocate memory for the first node, I assign its link to NULL and its value to 2.
Node *head = getNode();
head -> link = NULL;
head -> info = 2;
Then comes the prepend function:
void prepend(Node **headPointer, int value) {
Node *new_node;
new_node = getNode();
new_node -> info = value;
new_node -> link = *headPointer;
*headPointer = new_node;
}
I am using the following function call:
prepend(&head, 5)
As you can see, I'm using a pointer to a pointer. I store the address of head in headPointer. I create new_node and allocate memory to it. I assign its info field, then the link field gets the dereferenced headPointer, which is the value stored in head, which is in turn the address for the chunk of memory in the Heap segment.
So, I basically link new_node to head, right? Now comes the confusing part, for me. The dereferenced headPointer, which is head's pointed chunk of memory in the Heap segment, gets the value stored in new_node which is another address from the Heap segment, I guess. Then, both new_node and headPointer go out of scope. (?)
How does this all add up? Is there a simpler way to describe the situation or implement prepending?

Then, both new_node and headPointer go out of scope. (?)
At the end of the prepend() newnode goes out of scope but not the memory allocated since it is allocated on heap.If it were something like int a, then at the end of prepend(), a is gone out of scope and referencing a after that would be undefined behavior.Please read this and this to know about heap.
Also since you pass head of the list as pointer to pointer, when you change what the headPointer points to inside prepend(), it is reflected outside the function so you still have a pointer to the head of the list.
|2|-->NULL
^
|
head
After call to prepend()
1) |5|--> |2|-->NULL
^
|
head
2) |5|----> |2|--->NULL
^
|
head
Also remeber to have some way of accessing the heap allocated memory in order to deallocate it.If you don't have any means of pointing to a memory allocating on a heap, then you are left with a memory leak.

Related

Assignment to Heap Allocated Memory

I was writing a singly-linked list in C and cannot understand this following code.
#include <stdlib.h>
typedef struct ListNode {
int val;
struct ListNode* next;
} ListNode;
int main() {
/*Say I allocate this list to say 1->2->3->4->NULL*/
ListNode* node = malloc(sizeof(ListNode));
ListNode* n1 = node; // An ordinary pointer
ListNode* n_heap = malloc(sizeof(ListNode)); // A heap allocated pointer
n_heap = node->next; // 2->3->4->NULL
n1->next = NULL;
}
Now from the above example, I had assumed that n_heap to be NULL as well. But even after n1->next = NULL;, n_heap is still 2->3->4->NULL. So is the node->next copied to n_heap? or is it that n_heap now points to the original heap located node->next, and the n1->next now set to NULL? Does this mean node wasn't initially the owner of node->next data?
int main() {
/*Say I allocate this list to say 1->2->3->4->NULL*/
ListNode* node = malloc(sizeof(ListNode));
node is a pointer to dynamically allocated data. On a C implementation with a stack / heap distinction, that will be on the heap.
ListNode* n1 = node; // An ordinary pointer
n1 now points to the same dynamically-allocated data that node does. It is no more or less "ordinary" than node is.
ListNode* n_heap = malloc(sizeof(ListNode)); // A heap allocated pointer
(On success) n_heap points to different dynamically allocated data. The pointer itself has the same scope and storage duration as node and n1, and is exactly as "ordinary" as they are.
n_heap = node->next; // 2->3->4->NULL
Now the memory to which n_heap previously pointed is leaked, because no pointers to it remain. n_heap points to the same memory that node->next does. Which is the same memory that n1->next does.
n1->next = NULL;
Now the value of the pointer object accessible as n1->next (and also as node->next) is set to NULL. This has no effect on the data to which that pointer object previously pointed, nor on variable n_heap, which still points to the data in question.
}
Now from the above example, I had assumed that n_heap to be NULL as
well. But even after n1->next = NULL;, n_heap is still 2->3->4->NULL.
You are failing to distinguish between pointer objects and the objects to which their values point. Assigning NULL to n1->next sets the value of one pointer object. It does nothing to the object to which the old pointer value pointed. It does nothing to other, distinct pointers to the same object. And on the other hand, n_heap is not 2->3->4->NULL, It is a pointer distinct from all those nodes, whose value points to the node containing 2.
So is the node->next copied to n_heap?
Yes, that's what an assignment does, but not in the sense I think you mean. Again, assigning n_heap = node->next copies the pointer value stored in node->next to n_heap. Those two distinct pointer objects then both contain values pointing to the same data.
or is it that n_heap now points
to the original heap located node->next, and the n1->next now set to
NULL?
Yes and no. Again, neither the object designated by n_heap nor the one designated by node->next contain the data you are describing as 2->3->4->NULL. Instead, they both contain the address of the node containing the first of those values.
Does this mean node wasn't initially the owner of node->next
data?
It depends on what you mean by "owner". Certainly neither node nor *node is a container of the data to which node->next points. As the term "owner" is usually applied to pointers, it is about responsibility for freeing the pointed to data, not about storage layout. Responsibility to free is a question of data and control flow in the program, not a property of the data itself.

If two pointers point to the same memory address, do you only need to use free(ptr) once or twice?

Say we have a struct...
struct node{
int data;
struct node *next;
};
struct node *new_node = malloc(sizeof(node));
struct node *temp_node = new_node;
If I use free...
free(temp_node);
Is new_node free as well (since the address no longer exists) or does new_node simply point to NULL (in which case I would need to free new_node as well) ?
Cheers!
You don't free pointers but the memory block(s) that you allocated - whose address a pointer points to.
With malloc, it returns pointer to a chunk of memory allocated that looks like this:
+--------+
| |
new_node --> | 0x1000 |
| |
+--------+
If 0x1000 is the starting address of that block of memory, that's what new_node points to (i.e., new_node == 0x1000).
When you assign new_node to temp_node, temp_node points to the same block of memory (i.e., temp_node == 0x1000):
+--------+
| |
new_node --> | 0x1000 | <-- temp_node
| |
+--------+
But there's just one block of memory that you have allocated. So once you free it via either pointer, the other is automatically invalidated and you are no longer allowed access that block of via either pointer.
In the same way, you can assign it to any number of pointers but as soon as it's free'd via one pointer, it's done. That's why care is needed when copying pointers around (because if you free one, it may still be inadvertently used).
P.S.: Free'd pointer may or may not point to NULL afterwards - it's simply undefined behaviour to access free'd memory.
The way to think about is like: pointers point to a memory location.
free() returns memory pointed to by a pointer back to the system.
Thus if you have two pointers - once free is called the memory is returned to the system. You still have two pointers. And they still point to the same memory location. It's just now it is not your memory but system's :)
In short - free as many times as you malloc'd.
There are plenty of exceptions, but as a general rule, there should be exactly one call to free for every call to malloc.
When you assign one pointer variable to another, you are copying the pointer value -- you are not allocating more memory. So a pointer copy does not imply the need for a second call to free.
For one memory address, you only need to call free once.
Once you call free, that tells the operating system that the memory you allocated can be used again. You do not need to call free on it a second time (and shouldn't, because that's undefined behavior).
Because you are freeing memory and I see just one malloc here, you should only free() once.
In general, for each malloc, there has to be one and only one free.
Otherwise, you will get a double free error.
A few notes on the code
The declarations struct *next and struct *temp_node point to an
unnamed struct that has no instances.
node is just a structure tag used to name the structure and
cannot be used in an expression like this malloc(sizeof(node)).
If this is what you originally meant:
struct node{
int data;
struct node *next;
};
struct node * new_node = malloc(sizeof(struct node));
struct node * temp_node = new_node;
Then the answers provided before this post are completely accurate.
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node * next;
}* first=NULL;
int main()
{
first=(struct Node *)malloc(sizeof(struct Node));
first->data=5;
first->next=NULL;
struct Node * t=first;
printf("%d",t->data);
free(first);
printf(" %d ",t->data);
}
In this scenario though First is freed in main then too t prints the data.
Which says that the allocated memory block can be further used for the different allocation but it's still there and can be used if a pointer is pointing to it.
But in case new allocation is made then that memory block may be allocated to the other variable. Hence after freeing the memory it's behaviour is undefined.

If you malloc a struct* does it create local variables?

Feel free to edit the title if it doesn't make sense. So I've been using malloc for a while without really being completely sure of how it works. If I create an int pointer and set it equal to malloc(10*sizeof(int), I figure that it allocates a block in memory of 10 times the size of one int, then returns the starting address of this allocation, but nothing is actually initialized within that memory yet. Am I OK so far?
Now say I create a struct for linked list nodes like this:
typedef struct node_ {
int data;
struct node_ *next;
} Node;
and then have a create linked list function:
Node* LLCreate(int data) {
Node *head = malloc(sizeof(Node));
if ( head != NULL) {
head->next = NULL; // don't get how malloc created `next`
head->data = data; // or `data`
}
return head;
}
What I don't get, and can't seem to google appropriately, is, if head is just the starting location in memory of an empty block of size Node, and not an actual variable, how does it have the local variables of next and data?
Maybe this is a nonsensical question because I have some fundamental misunderstanding of what's going on here, but if anyone understands what I'm trying to ask and could clear this up for me I'd really appreciate it.
It doesnt have local variables like next and data.
next and data are just used to calculate the address relative to from where node is pointing.
When you do node->data then the address is calculated as address pointed by node + (total bytes required by any metadata for malloc) + 0 because data is the first member of node struct.
And when you do node->next then the address is calculated as address pointed by node + (total bytes required by any metadata for malloc) + sizeof(data) , because next is the second member of the node struct that comes after data member.

Returning local pointer

My question is an extension of this: Returning pointer to a local structure
I wrote the following code to create an empty list:
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
I just read that returning pointers to local variables is useless, since the variable will be destroyed when the function exits. I believe the above code is returning a NULL pointer, so I don't think it's a pointer to a local variable.
Where is the memory allocated to the pointer in this case. I didn't allocate any memory on the heap, and it should be on the stack, as an automatic variable. But what happens when the code exits (to the pointer), if I try to use it in the program, by assigning this pointer some pointees / de-referencing and alike?
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
is equivalent to:
struct node* create_empty_list(void)
{
return NULL;
}
which is perfectly fine.
The problem would happen if you had something like:
struct node head;
return &head; // BAD, returning a pointer to an automatic object
Here, you are returning the value of a local variable, which is OK:
struct node* create_empty_list()
{
struct node* head = NULL;
return head;
}
The value of head, which happens to be NULL (0), is copied into the stack before function create_empty_list returns. The calling function would typically copy this value into some other variable.
For example:
void some_func()
{
struct node* some_var = create_empty_list();
...
}
In each of the examples below, you would be returning the address of a local variable, which is not OK:
struct node* create_empty_list()
{
struct node head = ...;
return &head;
}
struct node** create_empty_list()
{
struct node* head = ...;
return &head;
}
The address of head, which may be a different address every time function create_empty_list is called (depending on the state of the stack at that point), is returned. This address, which is typically a 4-byte value or an 8-byte value (depending on your system's address space), is copied into the stack before the function returns. You may use this value "in any way you like", but you should not rely on the fact that it represents the memory address of a valid variable.
A few basic facts about variables, that are important for you to understand:
Every variable has an address and a value.
The address of a variable is constant (i.e., it cannot change after you declare the variable).
The value of a variable is not constant (unless you explicitly declare it as a const variable).
With the word pointer being used, it is implied that the value of the variable is by itself the address of some other variable. Nonetheless, the pointer still has its own address (which is unrelated to its value).
Please note that the description above does not apply for arrays.
As others have mentioned, you are returning value, what is perfectly fine.
However, if you had changed functions body to:
struct node head;
return &head;
you would return address (pointer to) local variable and that could be potentially dangerous as it is allocated on the stack and freed immediately after leaving function body.
If you changed your code to:
struct node * head = (struct node *) malloc( sizeof( struct node ) );;
return head;
Then you are returning value of local value, that is pointer to heap-allocated memory which will remain valid until you call free on it.
Answering
Where is the memory allocated to the pointer in this case. I didn't
allocate any memory on the heap, and it should be on the stack, as an
automatic variable. But what happens when the code exits (to the
pointer), if I try to use it in the program, by assigning this pointer
some pointees / de-referencing and alike?
There is no memory allocated to the pointer in your case. There is memory allocated to contain the pointer, which is on the stack, but since it is pointing to NULL it doesn't point to any usable memory. Also, you shouldn't worry about that your pointer is on the stack, because returning it would create a copy of the pointer.
(As others mentioned) memory is allocated on the stack implicitly when you declare objects in a function body. As you probably know (judging by your question), memory is allocated on the heap by explicitly requesting so (using malloc in C).
If you try to dereference your pointer you are going to get a segmentation fault. You can assign to it, as this would just overwrite the NULL value. To make sure you don't get a segmentation fault, you need to check that the list that you are using is not the NULL pointer. For example here is an append function:
struct node
{
int elem;
struct node* next;
};
struct node* append(struct node* list, int el) {
// save the head of the list, as we would be modifying the "list" var
struct node* res = list;
// create a single element (could be a separate function)
struct node* nn = (struct node*)malloc(sizeof(struct node));
nn->elem = el;
nn->next = NULL;
// if the given list is not empty
if (NULL != list) {
// find the end of the list
while (NULL != list->next) list = list->next;
// append the new element
list->next = nn;
} else {
// if the given list is empty, just return the new element
res = nn;
}
return res;
}
The crucial part is the if (NULL != list) check. Without it, you would try to dereference list, and thus get a segmentation fault.

Free whole list and set head to NULL

Here's the task. It is given a linked list, free all the memory and set head to NULL.
This is my function:
void free_list(struct Node *node)
{
while (node)
{
free(node);
node=node->next;
}
}
It outputs no error, just wont do anything. And another thing, how to check if the memory was freed ?
Hints at what's going wrong rather than sample code since this is homework...
You can't reliably access node after freeing it. Store the value of node->next before freeing.
You're passing the Node pointer by value. If you want to NULL the caller's pointer, use a pointer to a pointer instead.
There is no portable way to check the memory was freed, but you could investigate tools like valgrind if you want to check that no memory has been leaked when your program exits.
Note that you can't check the contents of memory locations to reassure yourself that the memory has been freed. Calling free merely passes ownership of the memory back to the system; it may get reallocated (and its values updated) at any point in future.
You should get the next node before freeing the current one
If you want to assign to the head, you need to pass its address to the function.
void free_list(struct Node **head)
{
struct node *nnode, *cnode;
cnode = *head;
while (cnode)
{
nnode = cnode->next;
free(cnode);
cnode = nnode;
}
*head = NULL;
}
call it like this..,,
free_list(&head);
EDIT:
You are accessing the same node node=node->next; after freeing it, You must have to store next pointer before freeing the node.

Resources