What is the problem with this piece of code? - c

I am writing for deleting the last node of a doubly linked list. But, every time this function is giving me segmentation fault when I have 2 or more than 2 elements in the list.
void deleteEnd()
{
struct node *ptr;
if(head==NULL)
printf("\nList is empty.First add some numbers");
else if(head->next==NULL)
{
head = NULL;
free(head);
}
else
{
ptr = head;
while(ptr->next != NULL)
{
ptr = ptr -> next;
}
ptr -> prev -> next = NULL;
free(ptr);
}
}

Normally, when you are deleting a node from a linked list it is a good practice to pass a reference to the first node of the list as an argument of the function. In your case you are not showing us where the head is coming from, and I think that it could be quite a useful info, and I bet that the error hides there.
That is how the implementation could look like:
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
// insert end node: this is for testing purposes
struct node *insertEnd(struct node *head, int value) {
struct node *ptr, *new_node;
ptr = head;
new_node = (struct node *) malloc(sizeof(struct node));
new_node->data = value;
new_node->next = NULL;
// The list is empty
if (head == NULL) {
head = new_node;
return head;
}
// Non empty list
while (ptr->next != NULL) {
ptr = ptr->next;
}
ptr->next = new_node;
return head;
}
// delete end node
struct node *deleteEnd(struct node *head) {
struct node *ptr, *preptr;
ptr = head;
preptr = NULL;
// The list is empty
if (head == NULL) {
printf("The list is empty. Nothing to delete.\n");
return head;
}
while(ptr->next != NULL) {
preptr = ptr;
ptr= ptr->next;
}
free(ptr);
if (preptr == NULL) {
head = NULL;
}
else {
preptr->next = NULL;
}
return head;
}
int main(void) {
struct node *llist;
llist = NULL;
llist = insertEnd(llist, 10);
llist = insertEnd(llist, 20);
llist = insertEnd(llist, 30);
llist = deleteEnd(llist);
llist = deleteEnd(llist);
llist = deleteEnd(llist);
return 0;
}

It is always a great idea to maintain a global variable head and update it after every push/pop. See this:
struct node
{
int data;
struct node *next;
};
struct node *start = NULL;
struct node *deleteEnd(struct node *start)
{
struct node *ptr = start;
if(ptr==NULL)
{
printf("\nList is empty!!!");
return start;
}
else if(ptr->next == NULL)
{
free(ptr);
start = NULL;
return start;
}
else
{
while((ptr->next)->next!=NULL) //by doing this you dont have to maintain a prev pointer
{
ptr=ptr->next;
}
struct node *temp = ptr->next;
free(temp);
ptr->next = NULL;
return start;
}
}
Hope this helps!!!

Related

Implementing LinkedList in C

I implemented a linked list for my school assignment but when I try to print out value of a node, the first node always get printed with memory address and I can't figure out why it's happening. 
I tried debugging and as I assign tail of list to the new node, the value of the node gets printed out as memory address.
Why is this happening?
int AppendLinkedList(LinkedListPtr list, int value) {
LinkedListNodePtr newNode = CreateLinkedListNode(value);
if (list->head == NULL) {
list->head = newNode;
list->tail = newNode;
return 0;
}
LinkedListNodePtr tailCopy = list->tail;
newNode->prev = tailCopy;
tailCopy->next = newNode;
list->tail = newNode;
return 0;
}
LinkedListNode *CreateLinkedListNode(int data) {
LinkedListNodePtr newNode;
newNode = (LinkedListNodePtr)malloc(sizeof(LinkedListNode));
newNode->data = data;
printf("%d\n", data);
return newNode;
}
int main() {
LinkedListPtr list = CreateLinkedList();
int data = 5;
AppendLinkedList(list, data);
}
typedef struct ll_node {
int data; // Data this node holds
struct ll_node *next; // next node in list, or NULL
struct ll_node *prev; // prev node in list, or NULL
} LinkedListNode, *LinkedListNodePtr;
typedef struct ll_head {
uint64_t num_elements; // # elements in the list
LinkedListNodePtr head; // head of linked list, or NULL if empty
LinkedListNodePtr tail; // tail of linked list, or NULL if empty
} *LinkedListPtr;
LinkedListPtr CreateLinkedList() {
LinkedListPtr list;
list = (LinkedListPtr)malloc(sizeof(LinkedListPtr));
if (list == NULL) {
return NULL;
}
return list;
}
There are multiple problems in your code:
you do not initialize the prev and next members to NULL in CreateLinkedListNode().
the allocation size is incorrect in CreateLinkedList(): you should use:
LinkedList *list = malloc(sizeof(*list));
and you should initialize the members num_elements to 0 and head and tail to NULL.
the order of definitions is inadequate and the header files are missing.
AppendLinkedList() does not update num_elements.
More generally, hiding pointers behind typedefs is error prone. The code is much more readable with the explicit pointer syntax.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
typedef struct ll_node {
int data; // Data this node holds
struct ll_node *next; // next node in list, or NULL
struct ll_node *prev; // prev node in list, or NULL
} LinkedListNode;
typedef struct ll_head {
uint64_t num_elements; // # elements in the list
LinkedListNode *head; // head of linked list, or NULL if empty
LinkedListNode *tail; // tail of linked list, or NULL if empty
} LinkedList;
LinkedList *CreateLinkedList() {
LinkedList *list = malloc(sizeof(*list));
if (list != NULL) {
list->num_elements = 0;
list->head = NULL;
list->tail = NULL;
}
return list;
}
LinkedListNode *CreateLinkedListNode(int data) {
LinkedListNode *newNode = malloc(sizeof(*newNode));
if (newNode != NULL) {
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
}
return newNode;
}
int AppendLinkedList(LinkedList *list, int value) {
if (list == NULL)
return -1;
LinkedListNode *newNode = CreateLinkedListNode(value);
if (newNode == NULL)
return -1;
if (list->head == NULL) {
list->head = newNode;
} else {
LinkedListNode *tail = list->tail;
newNode->prev = tail;
tail->next = newNode;
}
list->tail = newNode;
list->num_elements += 1;
return 0;
}
int main() {
LinkedList *list = CreateLinkedList();
int data = 5;
AppendLinkedList(list, data);
}

I want to delete node from singly linked list using only one local pointer variable, what error in this code section?

I want to delete node from singly linked list using only one local pointer variable in C, the debugger stops on the free(cur) of the delete function without any error, but it runs normally in free(cur->next), why is this? What error in this code section?
struct node
{
int val;
struct node *next;
};
typedef struct
{
struct node *header;
} List;
void add(List *pList, int val)
{
struct node *new_node = malloc(sizeof pList->header);
if (new_node == NULL)
{
exit(EXIT_FAILURE);
}
new_node->val = val;
new_node->next = pList->header;
pList->header = new_node;
}
void delete(List *pList, int val)
{
struct node *cur = pList->header;
if (cur != NULL)
{
if (cur->val == val)
{
struct node *temp = cur->next;
//debug stop in free(cur) without any error,why?
free(cur);
cur = temp;
}
else
{
while (cur->next != NULL && cur->next->val != val)
{
cur = cur->next;
}
if (cur->next != NULL)
{
struct node *temp = cur->next->next;
// run normally, why?
free(cur->next);
cur->next = temp;
}
}
}
}
There is a problem in the add function: you pass the size of a pointer to malloc instead of the size of the node structure. A safer way to always pass the correct size is this:
struct node *new_node = malloc(sizeof *new_node);
As coded, the memory allocated is too small and you have undefined behavior when you initialize the structure, writing beyond the end of the allocated block.
There is another problem in the delete() function: you do not update pList->header when you free the first node.
Here is a modified block:
struct node {
int val;
struct node *next;
};
typedef struct {
struct node *header;
} List;
void add(List *pList, int val) {
struct node *new_node = malloc(sizeof(*new_node));
if (new_node == NULL) {
exit(EXIT_FAILURE);
}
new_node->val = val;
new_node->next = pList->header;
pList->header = new_node;
}
void delete(List *pList, int val) {
struct node *cur = pList->header;
if (cur != NULL) {
if (cur->val == val) {
pList->header = cur->next;
free(cur);
} else {
while (cur->next != NULL) {
struct node *temp = cur->next;
if (temp->val == val) {
cur->next = temp->next;
free(temp);
break;
}
cur = temp;
}
}
}
}

Segment Fault - Linked List in C

I am creating a basic linked list in C but I am getting a segment fault when my add to back function is called in my test file. It seems to have a problem because the head of the list is NULL, but I don't see why this is an issue. When I use the cgdb tool, it tells me the error is occurring on the line: prev->next = new_node. I have attached the full code below, please let me know what I am not seeing to fix this.
header file
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
typedef struct Node {
int data;
struct Node *next;
} Node;
Node *create_node(int data);
void free_list(Node *head);
void add_to_front(struct Node **head, int data);
void print_list(struct Node *head);
void reverse_list(struct Node **head);
void add_to_back(Node **head, int data);
#endif // LINKED_LIST_H
.c file
#include <stdio.h>
#include <stdlib.h>
#include "linked_list.h"
/* returns a new node whose data is set to DATA and next is set to NULL */
Node *create_node(int data) {
struct Node *new_node = malloc(sizeof(struct Node));
if (new_node == NULL) {
perror("Malloc failed\n");
}
new_node->data = data;
new_node->next = NULL;
return new_node;
}
/* Frees the list starting at HEAD */
void free_list(Node *head) {
while (head != NULL) {
Node *temp = head->next;
free(head);
head = temp;
}
}
/* Creates a new node whose data is set to DATA and adds it to the front of the
list pointed to by HEAD.
*/
void add_to_front(struct Node **head, int data) {
/* Check if the head is NULL to make sure that we do not dereference a NULL pointer
because that would result in a segfault */
if (head == NULL) return;
struct Node *new_node = create_node(data);
if (*head != NULL) {
/* The list is not empty */
/* The new node's next should point to the head */
new_node->next = *head;
}
/* We must set HEAD using the following line in order to change the original list */
*head = new_node;
/* The following line would not work because it would only change our local copy of HEAD */
/* head = new_node */
}
/* Prints out a linked list starting at HEAD */
void print_list(struct Node *head) {
struct Node *curr;
for (curr = head; curr != NULL; curr = curr->next) {
printf("%d->", curr->data);
}
printf("NULL\n");
}
/* Iteratively reverses a linked list whose first node is HEAD */
void reverse_list(struct Node **head) {
if (head == NULL || *head == NULL) {
return;
}
struct Node *curr = *head;
struct Node *next = (*head)->next;
curr->next = NULL;
while (next != NULL) {
struct Node *temp = next->next;
next->next = curr;
curr = next;
next = temp;
}
*head = curr;
}
/* Creates a new node with a data field set to DATA and adds the node
to the back of the list pointed to by HEAD */
void add_to_back(Node **head, int data) {
if (head == NULL || *head == NULL) {
return;
}
Node *new_node = create_node(data);
Node *prev;
for (Node *curr = *head; curr != NULL; curr = curr->next) {
prev = curr;
}
prev->next = new_node;
}
test file
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "linked_list.h"
int main(int argc, char **argv) {
printf("Running tests...\n\n");
Node *head = NULL;
/*********** reverse_list test ***********/
reverse_list(&head);
for (int i = 0; i < 5; ++i) {
add_to_front(&head, i);
reverse_list(&head);
}
int expected_values[] = {3, 1, 0, 2, 4};
Node *curr = head;
for (int i = 0; i < 5; ++i) {
assert(curr->data == expected_values[i]);
curr = curr->next;
}
free_list(head);
printf("Congrats! You have passed the reverse_list test!\n\n");
/************ add_to_back test ***********/
Node *head_2 = NULL;
add_to_back(&head_2, 15);
add_to_back(&head_2, 12);
add_to_back(&head_2, 18);
int expected_values_2[] = {15, 12, 18};
Node *curr_2 = head_2;
for (int i = 0; i < 3; ++i) {
assert(curr_2->data == expected_values_2[i]);
curr_2 = curr_2->next;
}
free_list(head_2);
printf("Congrats! All of the test cases passed!\n");
return 0;
}
The if statement in the function add_to_back
if (head == NULL || *head == NULL) {
return;
}
does not make a sense because it does not allow to append a node to an empty list.
The function can be defined for example like
void add_to_back( Node **head, int data )
{
if ( head == NULL ) return;
Node *new_node = create_node(data);
while ( *head ) head = &( *head )->next;
*head = new_node;
}
Or using your approach then
void add_to_back( Node **head, int data )
{
if ( head == NULL ) return;
Node *new_node = create_node(data);
Node *prev = NULL;
for ( Node *curr = *head; curr != NULL; curr = curr->next )
{
prev = curr;
}
prev == NULL ? ( *head = new_node ) : ( prev->next = new_node );
}
In general this statement
if ( head == NULL ) return;
is also redundant. If the user will pass a null pointer then the function just will have undefined behavior.
On the other hand the function create_node should be written the following way
Node * create_node( int data )
{
struct Node *new_node = malloc(sizeof(struct Node));
if ( new_node != NULL )
{
new_node->data = data;
new_node->next = NULL;
}
return new_node;
}
Correspondingly for example the function add_to_back can be written like
int add_to_back( Node **head, int data )
{
Node *new_node = create_node( data );
int success = new_node != NULL;
if ( success )
{
Node *prev = NULL;
for ( Node *curr = *head; curr != NULL; curr = curr->next )
{
prev = curr;
}
prev == NULL ? ( *head = new_node ) : ( prev->next = new_node );
}
return success;
}
The problem is in the add_to_back() function. Because of first line in that function if (head == NULL || *head == NULL) { return; }, it will keep returning and not executing rest of the code. So, if you print your list after three calls of the add_to_back function, your list has nothing but NULL and that is the reason you are getting a segmentation fault. I did the following change and your code is working fine
`
void add_to_back(Node **head, int data) {
Node *new_node = create_node(data);
if (*head == NULL){
*head = new_node;
return;
}
Node *prev;
for (Node *curr = *head; curr != NULL; curr = curr->next) {
prev = curr;
}
prev->next = new_node;
}
`

How to make to linked list in C?

Using two linked lists or something similar, I want to add "ABCD", then I want to get "ABDC".
I'm not sure if my code is right, I'm a noob in C.
The code should have pointer and malloc. I made it like this:
struct Node
{
char data;
struct Node* next;
};
struct Node* newNode(char data)
{
struct Node* node = (struct Node*)malloc(sizeof(struct Node));
node->data = data;
node->next = NULL;
return node;
}
struct Node* constructList()
{
struct Node* first = newNode(1);
struct Node* second = newNode(2);
struct Node* third = newNode(3);
struct Node* forth = newNode(4);
struct Node* head = first;
first->next = second;
second->next = third;
third->next=forth;
return head;
}
void printList(struct Node* head)
{
struct Node* ptr = head;
while (ptr)
{
printf("%d -> ", ptr->data);
ptr = ptr->next;
}
printf("NULL");
}
Your code is fine as shown, though it lacks error handling (and why are you using char to store integers?).
Do make sure you free() the nodes when you are done using them, eg:
void freeList(struct Node* head)
{
struct Node* ptr = head;
struct Node* next;
while (ptr)
{
next = ptr->next;
free(ptr);
ptr = next;
}
}
I would suggest changing constructList() to use a loop, eg:
struct Node* constructList()
{
struct Node *head = NULL;
struct Node **ptr = &head;
for (int value = 1; value <= 4; ++value)
{
*ptr = newNode(value);
if (*ptr == NULL)
{
freeList(head);
return NULL;
}
ptr = &((*ptr)->next);
}
return head;
}
You could then generalize constructList() to take an array of integers, or a string, as input and loop through that input adding a new node for each value. For example, try this:
struct Node
{
char data;
struct Node* next;
};
struct Node* newNode(char data)
{
struct Node* node = (struct Node*) malloc(sizeof(struct Node));
if (node)
{
node->data = data;
node->next = NULL;
}
return node;
}
void freeList(struct Node* head)
{
struct Node* ptr = head;
struct Node* next;
while (ptr)
{
next = ptr->next;
free(ptr);
ptr = next;
}
}
struct Node* constructList(const char *str)
{
struct Node *head = NULL;
struct Node **ptr = &head;
char ch;
if (str)
{
while ((ch = *str++) != '\0')
{
*ptr = newNode(ch);
if (*ptr == NULL)
{
freeList(head);
return NULL;
}
ptr = &((*ptr)->next);
}
}
return head;
}
void printList(struct Node* head)
{
struct Node* ptr = head;
while (ptr)
{
printf("%c -> ", ptr->data);
ptr = ptr->next;
}
printf("NULL");
}
struct Node *list = constructList("ABCD");
printList(list);
freeList(list);

Linked list reverse function leads to infinite printing loop

I was writing a C code to reverse a link list. I got into one problem.
If I do not make my next pointer NULL my reverse function works fine, but if I make it null the linked list always keeps printing in the while loop.
Below is the correct program, which works fine.
But if I make *next = NULL, the display function will keep printing in the while loop.
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *next;
} *head;
/*************************************************************/
/* */
/* create - Function to create Nodes and add them at last */
/* */
/*************************************************************/
int create(int data)
{
struct node *temp,*ptr = NULL;
//int data = 0;
ptr = head;
//Printf(" Enter the Data for Node : ");
//scanf(" %d ", data);
temp = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL) {
// this is the first node
temp->next = NULL;
temp->data = data;
head = temp;
} else {
// this is not the first node
while (ptr != NULL) {
if (ptr->next == NULL) {
temp->next = NULL;
temp->data = data;
ptr->next = temp;
break;
}
ptr = ptr->next;
}
}
return 0;
}
/*************************************************************/
/* */
/* create_in_front - Function to add Node in Front */
/* */
/*************************************************************/
int create_in_front(int data)
{
struct node *temp,*ptr = NULL;
ptr = head;
temp = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL) {
// this is the first node
temp->next = NULL;
temp->data = data;
head = temp;
} else {
// this is not the first node
temp->next = ptr->next;
temp->data = data;
head = temp;
}
return 0;
}
/*************************************************************/
/* */
/* create_in_between - Function to add Node in between nodes*/
/* */
/*************************************************************/
int create_in_between(int data,int pos)
{
struct node *temp, *ptr = NULL;
int i = 0;
ptr = head;
temp = (struct node *)malloc(sizeof(struct node));
temp->data = data;
for (i = 0; i < pos; i++) {
if (i == pos-1) {
temp->next = ptr->next;
ptr->next = temp;
}
ptr = ptr->next;
}
return 0;
}
/*************************************************************/
/* */
/* delete_in_between - Function to add Node in between nodes*/
/* */
/*************************************************************/
delete_in_between(int pos)
{
struct node *ptr, *prev = NULL;
ptr = head;
int i = 0;
for (i = 0; i < pos; i++) {
if (i == pos-1) {
prev = ptr->next;
free(ptr);
break;
}
prev = ptr;
ptr = ptr->next;
}
return 0;
}
/*************************************************************/
/* */
/* reverse - Function to reverse link list */
/* */
/*************************************************************/
int reverse()
{
struct node *prev = NULL;
struct node *curr = head;
struct node *next = NULL;
curr = head;
while (curr != NULL) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
head = prev;
return 0;
}
/*************************************************************/
/* */
/* display - Function to diplay link list */
/* */
/*************************************************************/
// Function to display Link List
int display()
{
struct node *temp = head;
while (temp != NULL) {
printf("%d->",temp->data);
temp = temp->next;
}
return 0;
}
int main()
{
create(10);
create(20);
create(30);
create(40);
create(50);
create_in_front(34);
create_in_between(55,2);
//delete_in_between(4);
reverse();
display();
return 0;
}
Let me know the logic behind this.
Function create_in_front() is bogus: temp->next = ptr->next; should be changed to temp->next = ptr;
create_in_between() does not handle the case of pos==0.
delete_in_between() is completely dysfunctional: the node is frees but its predecessor still points to it.
reverse() seems correct to me, it could be simplified this way:
int reverse() {
struct node *prev = NULL;
struct node *curr = head;
while (curr != NULL) {
struct node *next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
head = prev;
return 0;
}
The problem seems unrelated to your modifying the reverse() function, maybe a side effect of bugs in the other functions.
Your reverse() function seems correct, but the rest of the code is somewhat overcomplicated. Try something like this:
void create(int data) {
struct node *temp = malloc(sizeof(struct node));
if (temp != NULL) {
temp->next = NULL;
temp->data = data;
if (head == NULL) { // this is the first node
head = temp;
} else {
// this is not the first node
struct node *last = head;
while (last->next)
last = last->next;
last->next = temp;
}
}
}
void create_in_front(int data) {
struct node *temp = malloc(sizeof(struct node));
if (temp != NULL) {
temp->next = head;
temp->data = data;
head = temp;
}
}

Resources