Pointer issue on linked list - c

As most beginners in C, I'm implementing a simple linked list.
Each node for the list is defined as so:
typedef struct list_node {
void * data;
struct list_node * next;
} list_node_t;
I made a test program to create and iterate a list. Creation and insertion operations work correctly. However, the code below is giving me problems:
list_node_t * node = NULL;
list_iter_reset(list, node);
if (node == NULL) {
printf("Node is NULL.\n");
} else {
printf("Node is not NULL.\n");
}
fflush(stdout);
The function list_iter_reset() is defined below. Please note that list->head does not point to NULL, as I have inserted nodes in the list previously.
void list_iter_reset(list_t list, list_node_t * node)
{
node = list->head;
if (node == NULL) {
printf("Node is NULL.\n");
} else {
printf("Node is not NULL.\n");
}
fflush(stdout);
}
The output from executing that code is as follows:
Node is not NULL.
Node is NULL.
Since I'm passing a pointer to the node to the function, why is the created node in the test program still pointing to NULL after that function call?
I'm guessing a simple pointer arithmetic aspect went over my head here. I've been looking around and could not find a similar issue.
Thank you in advance.

The function is modifying the value of the pointer, but that never gets back to the caller. It seems you want to be passing a pointer to pointer (list_node_t**) to the function, not a regular pointer.
void list_iter_reset(list_t list, list_node_t** node)
{
*node = list->head;
...
}
The reasoning for this is that while a pointer is all you need to change the value of the thing it's pointing to, here you are trying to change the value of the pointer itself, i.e. where this pointer is pointing to.
As a comparison, imagine you would be passing an int to the function and expecting the function to modify that int. You would of course have to pass an int*, right? This is the same thing, except replace int with node*, and so you need to pass a pointer to that type, which in this case is node**

Related

What is the correct syntax of Delete(node ) for SLL in C?

Assuming the relevant header files, functions for Singly Linked List in C are declared.
Is the following definition of Delete() correct?
/* The Structure for SLL
typedef struct SLL
{
int data;
struct SLL *next;
}node;
Function Delete() deletes a node*/
void Delete( node **head)
{
node *temp, *prev;
int key;
temp = *head;
if(temp == NULL)
{
printf("\nThe list is empty");
return;
}
clrscr();
printf("\nEnter the element you want to delete:");
scanf("%d", &key);
temp = search( *head , key);//search()returns the node which has key
if(temp != NULL)
{
prev = get_prev(*head, key);
if(prev != NULL)
{
prev->next = temp->next;
free(temp);
}
else
{
*head = temp->next;
free(temp);
}
printf("\nThe node is deleted");
getch();
}
}
1) What happens if I replace(node ** head) with (node *head)?
2) What happens if I replace void Delete (node **head) with node
*Delete(node *head)?
3) Is there an alternate way to delete a node in C?
Thanks in advance
This isn't a tutorial site, but here goes...
You do know that arguments in C are passed by value? Meaning the value is copied.
For example:
void some_function(int a)
{
// ...
}
When calling the function above, like
int x = 5;
some_function(x);
Then the value in x is copied into the argument a in the function. If the code inside the function assigns to a (e.g. a = 12;) then you only modify the local variable a, the copy. It does not modify the original variable.
Now, if we want the function to modify x, then we must emulate pass by reference, which is done using pointers and the address-of operator:
void some_function(int *a)
{
*a = 12; // Modify where a is pointing
}
Now to call that, we don't create a pointer variable and pass that (though it's possible as well), instead we use the address-of operator & to pass a pointer to the variable:
int x = 5;
some_function(&x); // Pass a pointer to the variable x
The pointer &x will be passed by value (since that's the only way to pass arguments in C), but we don't want to modify the pointer, we want to modify the data where it points.
Now back to your specific function: Your function wants to modify a variable which is a pointer, then how do we emulate pass by reference? By passing a pointer to the pointer.
So if you have
node *head;
// Initialize head, make it point somewhere, etc.
Now since the Delete function needs to modify where head points, we pass a pointer tohead`, a pointer to the pointer:
Delete(&head);
The Delete function of course must accept that type, a pointer to a pointer to node, i.e. node **. It then uses the dereference operator * to get where the pointer is pointing:
*head = temp->next;
1) If you replace node** head with node* head you won't modify the original head pointer. You probably have a head somewhere that marks the beginning of the linked list. When you delete a node, there's a chance that you want to delete head. In that case you need to modify head to point to the next node in the linked list.
*head = temp->next;
free(temp);
This part of your code does exactly that. Here, temp == head. We want head to point to head->next, but if we pass in node* head to the function, the pointer will get modified but the changes will disappear because you're passing the pointer by value. You need to pass in &head which will be of type node ** head if you want the changes to be reflected outside of the function.
2) You will then change the function definition to return a void pointer (which is a placeholder pointer that can be converted to any pointer. Take care to not break any aliasing rules with this. But the problem from (1) remains, although, you could return a modified head, and assign it to the returned value. In that case define the function won't fit well with other cases where the head doesn't need to be modified. So you could return a pointer for head if it's modified or return NULL when it doesnt. It's a slightly messier method of doing things imho, though.
3) Yes, but that depends on the way a linked list is implemented. For the datatype shown here, the basic delete operation is as given.

Adding an element to the end of an ordered list

I have just started learning about dynamic structures in C.
The first type that I'm trying to learn is the ordered list. I have created a few functions - namely, adding nodes to the beginning of the list and printing elements in the list, but then I decided to write a function that allows me to add elements to the end of the list. My function looks like this:
typedef struct Node* Node;
void add_end(Node *head, int value) {
Node new;
new = malloc(sizeof(struct Node));
new -> value = value;
new -> next = NULL;
if(*head == NULL) {
*head = new;
}
else {
Node help = *head;
while(help->next != NULL) {
help = help->next;
}
help->next = new;
}
}
Some clarification: My structure consists of two fields - value (int) and next (pointer to the next node).
Thus, my questions are:
1) Notice my substitution using the variable called "help" - when I tried to do this without it,namely writing *head wherever help appears now, and this function did not work properly - it only added as much as two elements. For example, if I pushed 1, 3, 5, 7 to the list, only 5 and 7 would be added. Why was this so? I really can't see any reasonable explanation.
2) At first I tried passing an argument of the type Node to the function (Not Node* as I'm doing now) and the function did not put anything to the list. Once again, I can't see the difference between writing using the type Node and writing the variable without the asterisk. Could you explain it to me in layman terms?
I know that my question may have a trivial answer, but please, be understanding - this is my first encounter with pointers and they may be quite complex to comprehend.
To try to answer your two questions:
Because you typedefed Node as typedef struct Node* Node; what you are passing into add_end as the first parameter is a double pointer to struct Node (like struct Node**). You then dereference it in your while loop with Node help = *head; This means that the value of the actual pointer is going to change. If you didn't have the help pointer, you would then keep moving head until you get to the end of the list. This means that you would only have two elements in the list -- the head itself and its next element.
The answer to this has to do with your typedef again. When you pass Node, with your typedef, you are only passing a single pointer to struct Node representing head, which means that dereferencing it will not give you the pointer to head, but the structure itself, which means that neither your if or else statements will work as intended, as your intent is to compare pointers.
Your function type should probably be:
void add_end(Node **head, int value) {
^
because head is a pointer to Node
Like:
void add_end(Node **head, int value) {
Node* new; // NOTICE Node*
new = malloc(sizeof(struct Node));
// TODO - add check for new being NULL
new -> value = value;
new -> next = NULL;
if(*head == NULL) {
*head = new;
}
else {
Node help = *head;
while(help->next != NULL) {
help = help->next;
}
help->next = new;
}
}
and call it like:
Node* head = NULL;
add_end(&head, 42);
add_end(&head, 42);
add_end(&head, 42);

difference between (*head)->next and &(*head)->next in C [LINKED LIST]

typedef struct n {
int data;
struct n *next;
}node;
This function deletes all nodes with odd values (without freeing memory or additional variables) :
void deleteOdds (node **head) {
if (*head == NULL) {
return;
}
while ((*head)->next) {
if ((*head)->data % 2 != 0) {
*head = (*head)->next;
} else head = &(*head)->next;
}
}
I understand the logic (and already have some assumptions), but I'm not sure how to explain the difference between
*head =(*head)->next;
and
head = &(*head)->next;
thanks in advance!
The key to understanding what's going on is to see what is being assigned in each case:
*head = (*head)->next; assigns to whatever head is pointing to, which is either the original head pointer, the pointer to which is passed to the function, or a next of some prior node. We modify what is pointed to, while the pointer stays the same. This amounts to deleting from the list (and creating a memory leak in the process).
head = &(*head)->next assigns to the head itself, i.e. it modifies the pointer, not the thing that it points to. This amounts to skipping ahead in the list, without modifying it.
Note: head is not the ideal name for the variable. Since the variable points to head pointer only until the first "skip", a better name for it would be current.

delete a link list in c

I am solving a program to delete all the elements in a linked list and i encountered the following problem:
When i used a delete function with return type void, and checked if the start pointer is NULL in the main ,it wasn't and gives me absurd result
Code:
void deletes(struct node *start)
{
struct node *current,*next;
current=start;
while(current!=NULL)
{
next=current->link;
free(current);
start=next;
current=next;
}
start=NULL;
return ;
}
But if i change the return type, it works fine:
struct node *deletes(struct node *start)
{
struct node *current,*next;
current=start;
while(current!=NULL)
{
next=current->link;
free(current);
start=next;
current=next;
}
start=NULL;
return start;
}
Why is the start=NULL working in the first code?
My entire code is here
It's because in the first version you pass the list header by value, meaning the pointer to the head is copied, and you change only the copy in the function. Those changes are not visible after the function returns as no changes are made on the original copy.
Either do as you do in the second version, returning the result, or pass the pointer by reference, meaning you pass the address of the pointer (or a pointer to the pointer) using the address-of operator. Of course this means that you have to change the function as well:
void deletes(struct node **start)
{
struct node *current = *start;
/* Deleting the list... */
*start = NULL;
}
Call it like
struct node *list_head = ...;
deletes(&list_head);
Because in C, function arguments are passed by value. If you write start = NULL; inside a function, it will be ineffective outside of that function (it will only set the start pointer to NULL, which is essentially just a copy of the pointer value passed in, and it's local to the function.).
If you want to modify a function argument, you must pass a pointer to it. So,
void delete(struct node **start)
{
// ... delete ...
*start = NULL;
}
then
delete(&list);
would work.
It is because you should have (struct node **start), which can allow you to pass a pointer to the list so you can modify the list.
Currently you are only passing in a copy of the linked list to the function and therefore aren't changing the value of the actual list just a copy. Hence why when you return the copy you see the results

Linked List access violation in C

Im not sure i got the concept of Linked List properly. What im trying to do is to create a linked list which receives the integer 6 as it's first "data". instead i get this access violation error when trying to write in the integer into the first node's data. is there something specific i missed here?
///////////////////////////////
typedef struct List_Node
{
int data;
struct List_Node* next;
}List_Node;
typedef struct List
{
List_Node* head;
}List;
////////////////////////////////
List* create_list();
void print_list(List_Node *x);
void add_to_node(List_Node *a,int val);
////////////////////////////////
void main()
{
List *a = create_list();
List_Node *ind = a->head;
printf("List:\n");
add_to_node(ind,6);
}
void print_list(List_Node *a)
{
while(a != NULL)
{
printf("%d \n",a->data);
a = a->next;
}
return;
}
void add_to_node(List_Node *a,int val)
{
a->data = val;
}
struct List* create_list()
{
struct List* list = (List*) malloc(sizeof(List));
list->head = NULL;
return list;
}
The code is dereferencing a NULL pointer as a->head is NULL:
list->head = NULL; /* inside create_list()` and 'list' returned to ...*/
List_Node *ind = a->head; /* ... inside main(), and then passed to */
add_to_node(ind,6); /* add_to_node() ... */
a->data = val; /* and dereferenced inside add_to_node(). */
Dereferencing a NULL pointer is undefined behaviour. To correct, malloc() memory for a List_Node and assign to a->head. Recommend creating an add_node() function that allocates memory and assigns the new int value to newly malloc()d node.
Do I cast the result of malloc?
As all have pointed out, you are dereferencing a NULL pointer as your list->head contains NULL.
Another thing I should point out is that, you are creating a List. but not any Node. There is no node in the list. You have to allocate memory for a Node and then use it.
So, instead of add_to_node(), you may use a function add_node that will take the list or the head and the value as parameters, create a node(i.e. allocating memory for the node), set the value and add it to the list.
Also, in your case, the structure List is redundant as it contains only one member. instead you can simply use List_node* head.
What you are doing:
In create_list:
Allocating memory for a List pointer.
Setting the list's head to NULL.
In add_to_node:
Setting the specified node pointer's data element to the specified val.
In main:
Creating a List pointer a by calling create_list. This list has a NULL head.
Initializing a List_Node pointer, ind, to point to the created list's head (which is NULL).
Trying to set ind's data element to 6 by calling add_to_node.
This is where your program is causing the access violation exception.
ind = NULL. Therefore NULL->data = undefined behaviour.
What you should be doing:
In create_list:
Allocate memory for a List pointer, say linked_list.
Allocate memory for linked_list's head pointer.
For the linked_list's head, initialize data and the next pointer to 0 and NULL respectively.
In add_to_node:
Do the same thing you're doing now.
In main:
Create a List pointer a by calling create_list. Now, this list will have a valid, initialized NULL head, but with no meaningful data.
Set the list's head data by calling add_to_node(a->head, 6);.
Note: This will only ensure you have a head node in your list. Nowhere are you creating additional nodes.

Resources