linked list implemetation from scratch, segmentation fault - c

I want to implement linked list from scratch. I programmed it, but when I run it gives me a Segmentation fault in my node_adder(int data) function. I don't know what caused this.
struct linked_list
{
int value;
struct linked_list *next;
};
struct linked_list *head = NULL;
struct linked_list *tail = NULL;
void node_adder(int data)
{
struct linked_list *new = NULL;
new->value = data;
new->next = NULL;
if (head == NULL)
{
head = new;
tail = new;
printf("head added\n");
}
else
{
tail->next = new;
tail = new;
printf("tail added\n");
}
}

In node_adder(), you are not allocating any memory for the new node before accessing its members.
Try this instead:
struct linked_list
{
int value;
struct linked_list *next;
};
struct linked_list *head = NULL;
struct linked_list *tail = NULL;
void node_adder(int data)
{
struct linked_list *newNode;
// ADD THIS!!!
// in C:
newNode = malloc(sizeof(struct linked_list));
// in C++:
newNode = new linked_list;
//
newNode->value = data;
newNode->next = NULL;
if (head == NULL)
{
head = tail = newNode;
printf("head added\n");
}
else
{
tail->next = newNode;
tail = newNode;
printf("tail added\n");
}
}

This line initializes a pointer to a node to NULL:
struct linked_list *new = NULL;
Yet, no memory is allocated for this node. Therefore, in this line, attempting to assign a value to one of the members of the node leads to a segmentation fault:
new->value = data;
You probably meant to do this:
struct linked_list *new = malloc(sizeof(struct linked_list));
Of course, a corresponding free(..) should also be added when the allocated memory will no longer be used.

For starters it is a bad idea to declare global variables and define functions that depend on the global variables as you are doing
struct linked_list *head = NULL;
struct linked_list *tail = NULL;
because in this case you will not be able to define for example more than one list.
It is much better to introduce one more structure like for example
struct node
{
int value;
struct node *next;
};
struct linked_list
{
struct node *head;
struct node *tail;
};
As for your problem then you are using a null pointer to access memory that results in undefined behavior
void node_adder(int data)
{
struct linked_list *new = NULL;
new->value = data;
new->next = NULL;
//...
You need to allocate a node dynamically as for example
int node_adder(int data)
{
struct linked_list *new = malloc( sizeof( *new ) );
int success = new != NULL;
if ( success )
{
new->value = data;
new->next = NULL;
if (head == NULL)
{
head = new;
}
else
{
tail->next = new;
}
tail = new;
}
return success;
}
But using the approach I showed in the beginning of my answer You could write instead
struct node
{
int value;
struct node *next;
};
struct linked_list
{
struct node *head;
struct node *tail;
};
int node_adder( struct linked_list *list, int data )
{
struct node *new = malloc( sizeof( *new ) );
int success = new != NULL;
if ( success )
{
new->value = data;
new->next = NULL;
if (list->head == NULL)
{
list->head = new;
}
else
{
list->tail->next = new;
}
list->tail = new;
}
return success;
}
and in main you can write
int main( void )
{
struct linked_list list = { .head = NULL, .tail = NULL };
node_adder( &list, 10 );
//...
}

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);
}

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);

What is the problem with this piece of code?

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!!!

Why can't I set list pointer to minimal value (sorting words) in the list?

After inserting new element to the circular doubly linked list, when I set list pointer to minimal value, I can't see the value which was inserted to the list first (by creating the list), I can see only strange characters.
if I don't set the pointer to minimal value everything is working correctly, but I care to have the pointer set to minimal value.
struct Person {
char surname[N];
};
struct c_dll {
struct Person *data;
struct c_dll *next, *prev;
};
struct c_dll *create_list(char *surname) {
struct Person *new_person = (struct Person *)malloc(sizeof(struct Person));
strcpy(new_person->surname, surname);
struct c_dll *new_node = (struct c_dll *)malloc(sizeof(struct c_dll));
if (new_node != NULL) {
new_node->data = new_person;
new_node->next = new_node->prev = new_node;
}
return new_node;
};
struct c_dll *find_min(struct c_dll *node) {
char min_surname[20];
struct c_dll *result, *start;
strcpy(min_surname, node->data->surname);
start = result = node;
do {
if (strcmp(min_surname, node->data->surname) > 0) {
strcpy(min_surname, node->data->surname);
result = node;
}
node = node->next;
} while (start != node);
return result;
}
struct c_dll *find_next_node(struct c_dll *node, char *surname) {
node = find_min(node);
struct c_dll *start = node;
do {
if (strcmp(node->data->surname, surname) > 0)
break;
node = node->next;
} while (start != node);
return node;
}
void add_element(struct c_dll **list_ptr, char *surname) {
struct Person *new_person = (struct Person *)malloc(sizeof(struct Person));
strcpy(new_person->surname, surname);
struct c_dll *new_node = (struct c_dll *)malloc(sizeof(struct c_dll));
if (new_node != NULL) {
struct c_dll *node;
new_node->data = new_person;
node = find_next_node(&(*list_ptr), surname);
new_node->next = node;
new_node->prev = node->prev;
node->prev->next = new_node;
node->prev = new_node;
}
(*list_ptr) = find_min(&(*list_ptr));
}
The bug is in add_element: you pass &(*list_ptr) to find_min and find_next_node, which is incorrect and even a type mismatch that the compiler should diagnose.
You should just pass *list_ptr instead.
Here is a modified version:
int add_element(struct c_dll **list_ptr, const char *surname) {
struct Person *new_person = malloc(sizeof(*new_person));
if (new_person == NULL)
return -1;
strcpy(new_person->surname, surname);
struct c_dll *new_node = malloc(sizeof(*new_node));
if (new_node == NULL) {
free(new_person);
return -1;
} else {
struct c_dll *node;
new_node->data = new_person;
node = find_next_node(*list_ptr, surname);
new_node->next = node;
new_node->prev = node->prev;
node->prev->next = new_node;
node->prev = new_node;
*list_ptr = find_min(*list_ptr);
return 0;
}
}

Segmentation-Fault in C--Linked List

I understand that a set fault happens when you try to access memory out of the scope of your program, but I can't figure out where mine is happening, or why. I have a list defined:
*ListType list = NULL; ,
and then I add data to the List calling the add function in main:
addNodeToList(&list, song);
void addNodeToList(ListType **list, SongType *song)
{
printf("Starting Add");
NodeType *newNode = malloc(sizeof(NodeType));
NodeType *currNode;
currNode = (*list)->head;
newNode->data = song;
newNode->next = NULL;
if(currNode == NULL) {
printf("List is Empty");
(*list)->tail = newNode;
(*list)->head = newNode;
}
else {
(*list)->tail->next = newNode;
(*list)->tail = newNode;
}
}
The song I'm passing is properly initialized, and I can access it's elements, so I know that isn't causing the seg fault. Any help would be very appreciated!
typedef struct Song {
char title[MAX_STR];
char artist[MAX_STR];
char album[MAX_STR];
char duration[MAX_STR];
} SongType;
typedef struct Node {
struct Node *next;
SongType *data;
} NodeType;
typedef struct List {
NodeType *head;
NodeType *tail;
} ListType;
in main if you're declaring
ListType* list = NULL;
then in your function you're calling
(*list)->head
dereferencing (*list) is fine, but as soon as you do ->head it's trying to dereference that original NULL assignment. You need to allocate some space for list first.
It appears that your calling code contains:
ListType *list = NULL;
and you call your function like this:
addNodeToList(&list, song);
and in your addNodeToList() function you have:
NodeType *currNode;
currNode = (*list)->head;
which means you are dereferencing a null pointer (*list), which crashes your code. At minimum, check whether *list == NULL before setting currNode, but you'll need rethink the code to handle the case where list is a null pointer.
This code compiles, and runs. It avoids the problem by allocating the list when necessary:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
enum { MAX_STR = 64 };
typedef struct Song
{
char title[MAX_STR];
char artist[MAX_STR];
char album[MAX_STR];
char duration[MAX_STR];
} SongType;
typedef struct Node
{
struct Node *next;
SongType *data;
} NodeType;
typedef struct List
{
NodeType *head;
NodeType *tail;
} ListType;
void addNodeToList(ListType **list, SongType *song);
void addNodeToList(ListType **list, SongType *song)
{
printf("Starting Add\n");
assert(list != NULL);
if (*list == NULL)
{
*list = malloc(sizeof(**list));
assert(*list != 0); // Too lazy for production
(*list)->head = 0;
(*list)->tail = 0;
printf("List created\n");
}
NodeType *newNode = malloc(sizeof(NodeType));
newNode->data = song;
newNode->next = NULL;
printf("Node created\n");
NodeType *currNode = (*list)->head;
if (currNode == NULL)
{
printf("List is Empty\n");
(*list)->tail = newNode;
(*list)->head = newNode;
}
else
{
(*list)->tail->next = newNode;
(*list)->tail = newNode;
}
printf("Node added - all done\n");
}
int main(void)
{
ListType *list = NULL;
SongType data = { "Title", "Artist", "Album", "2m 30s" };
SongType *song = &data;
printf("Add song once\n");
addNodeToList(&list, song);
printf("Add song again\n");
addNodeToList(&list, song);
return 0;
}
Example run:
Add song once
Starting Add
List created
Node created
List is Empty
Node added - all done
Add song again
Starting Add
Node created
Node added - all done
The code leaks like a sieve; no memory is freed.
The problem deals with this statement
ListType *list = NULL;
You may not call addNodeToList for a list initialized such a way. Otherwise there will be undefined behaviour.
You should declare the list the following way
ListType list = { NULL, NULL };
Also the function addNodeToList can be declared simpler
void addNodeToList(ListType *list, SongType *song);
^^^^^^^^^^^^^^
You can call it like
addNodeToList( &list, song );
For example
void addNodeToList( ListType *list, SongType *song )
{
printf("Starting Add");
NodeType *newNode = malloc( sizeof( NodeType ) );
if ( newNode != NULL )
{
newNode->data = song;
newNode->next = NULL;
if ( list->tail == NULL )
{
printf( "List is Empty\n" );
list->head = list->tail = newNode;
}
else
{
list->tail->next = newNode;
list->tail = newNode;
}
}
}
Or if you want to have a pointer to list then you should write
ListType *list = malloc( sizeof( ListType ) );
list->head = list-tail = NULL;;
and then call the function the definition of which I showed the following way
addNodeToList( list, song );

Resources