Linked list in C – methods - c

Suppose we have doubly linked list of nodes
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node* next;
struct Node* prev;
} Node;
typedef struct LinkedList {
Node *first;
Node *last;
} LinkedList;
void initList(LinkedList* l) {
l->first = NULL;
l->last = NULL;
}
and I have to code method, which inserts a new node with given value to the end of the list and returns a pointer to the new node. My attempt follows:
Node *insert(LinkedList *list, int value) {
Node node;
node.value = value;
node.prev = list->last;
node.next = NULL;
if (list->last != NULL){
(list->last)->next = &node;
}else{
list->first = &node;
list->last = &node;
}
return &node;
}
It seems, that insertion in the empty list works, but it doesn't for a non-empty one.
(There are implementation tests, which tell me if an insertion was successful or not. I can post the codes of them, but don't think it's important).
So please, where are the mistakes?
There is a warning in the log (the 51st line is that with 'return &node')
C:\...\main.c|51|warning: function returns address of local variable [-Wreturn-local-addr]|
Is that serious problem? And how to remove it?
Thank you for the answers, but I think there is still a problem with non-empty lists, because according to the test, this fails:
void test_insert_nonempty(){
printf("Test 2: ");
LinkedList l;
initList(&l);
Node n;
n.value = 1;
n.next = NULL;
l.first = &n;
l.last = &n;
insert(&l, 2);
if (l.last == NULL) {
printf("FAIL\n");
return;
}
if ((l.last->value == 2) && (l.last->prev != NULL)) {
printf("OK\n");
free(l.last);
}else{
printf("FAIL\n");
}
}

Node node; is a local variable in your function insert. It is "destroyed" as soon as your function terminates and is not longer defined. Returning a pointer to local variable of a function is undefined behavior. You have to allocate dynamic memory. Dynamically allocate memory is reserved until you free it:
Node *insert(LinkedList *list, int value) {
Node *node = malloc( sizeof( Node ) ); // allocate dynamic memory for one node
if ( node == NULL )
return NULL; // faild to allocate dynamic memory
node->value = value;
node->prev = list->last;
node->next = NULL;
if ( list->first == NULL )
list->first = node; // new node is haed of list if list is empty
else // if ( list->last != NULL ) // if list->first != NULL then list->last != NULL
list->last->next = node; // successor of last node is new node
list->last = node; // tail of list is new node
return node;
}
Note to avoid memory leaks you have to free each node of the list, when you destroy the list.

You are returning address of non-static local variable which will vanish on returning from function, and dereferencing the address after returning from the function invokes undefined behavior.
You have to allocate some buffer and return its address.
Node *insert(LinkedList *list, int value) {
Node *node = malloc(sizeof(Node));
if (node == NULL) return NULL;
node->value = value;
node->prev = list->last;
node->next = NULL;
if (list->last != NULL){
(list->last)->next = node;
}else{
list->first = node;
list->last = node;
}
return node;
}

You have to allocate the new node dynamically.
Otherwise variable node in your function
Node *insert(LinkedList *list, int value) {
Node node;
//...
is a local variable of the function that will not be alive after exiting the function. As result any pointer to the variable used to access it will be invalid.
The function can look like
Node * insert( LinkedList *list, int value )
{
Node *node = malloc( sizeof( Node ) );
if ( node != NULL )
{
node->value = value;
node->prev = list->last;
node->next = NULL;
if ( list->last != NULL )
{
list->last->next = node;
}
else
{
list->first = node;
}
list->last = node;
}
return node;
}

Related

Why is memcheck reporting memory leak (double linked list)?

Working on simple double linked list.
struct list_node {
struct list_node *prev, *next;
int data;
};
struct list {
struct list_node *first, *last;
};
With creation and pushing as follows:
struct list *list_create(void) {
struct list *_list = malloc(sizeof(struct list));
if(_list == NULL) {
die("Memory allocation error for list_create\n");
}
_list->first = NULL;
_list->last = NULL;
return _list;
}
void list_push(struct list *list, int item_data) {
struct list_node *_node = malloc(sizeof(struct list_node));
if(_node == NULL) {
die("Memory allocation error for list_push\n");
}
_node->prev = NULL;
_node->next = NULL;
_node->data = item_data;
//empty list case
if(list->last == NULL) {
list->first = _node;
list->last = _node;
}
else {
_node->prev = list->last;
list->last->next = _node;
list->last = _node;
}
}
One push works but two successive ones cause memory leak - of size of a node.
What am I missing here?
For a completion here is my free for the list - and I am calling it in my code:
void list_destroy(struct list *list) {
if (list->first != NULL) {
struct list_node *p = list->first;
//if one node list
if(list->first == list->last) {
list->first = NULL;
list->last = NULL;
free(p);
p = NULL;
}
else {
while (p->next) {
struct list_node *q = p;
p = p->next;
free(q);
q = NULL;
}
}
}
free(list);
}
[SOLVED] Bug was in list_destroy - last time through while(p->next) left last p node not properly handled. Thanks everybody!
The memory leaks are produced by the function list_destroy in this while loop
while (p->next) {
struct list_node *q = p;
p = p->next;
free(q);
q = NULL;
}
The last node of the list is not freed due to the condition of the while loop
while (p->next) {
because for the last node the data member next is equal to NULL.
There is no sense to split the function into two parts. The function can look the following way
void list_destroy(struct list *list) {
while ( list->first )
{
struct list_node *p = list->first;
list->first = list->first->next;
free( p );
}
free( list );
}
It would be better to pass the pointer list by reference through a pointer to it as for example
void list_destroy(struct list **list) {
struct list_node *current = ( *list )->first;
while ( current )
{
struct list_node *p = current;
current = current->next;
free( p );
}
free( *list );
*list = NULL;
}
In this case after the function execution the pointer to the list in the caller will be equal to NULL.
Also you could write a function that clears the list. It can look the following way
void list_clear(struct list *list) {
while ( list->first )
{
struct list_node *p = list->first;
list->first = list->first->next;
free( p );
}
list->last = NULL;
}
Vlad from Moscow wrote a cleaner version.
I'd like to add "defensive programming".
It's dangerous to assume create() has been called before push() or destroy().
In fact, you can avoid a tough-to-find bug (using a pointer in the calling code after it's been free()'d.) Always check 'passed-in' arguments.
In the calling code:
myList = list_destroy( myList );
and return something that can be used.
list_t *list_destroy( list_t *pList ) {
assert( pList != NULL );
// iterate releasing alloc'd blocks...
return NULL;
}

Inserting a new node into a doubly linked list after a given node

This function inserts a new node after a given node in a doubly linked list.
It works well unless the list is empty or when given node is NULL.
I have tried to solve this problem by inserting the new node as head, but it doesn't add the new node or does problems when adding a second node.
void insert(Polynomial** node, int new_data, int pow) {
Polynomial* new_node = ( Polynomial*)malloc(sizeof( Polynomial));
new_node->num = new_data;
new_node->pow = pow;
if ((*node) == NULL) {
new_node->prev = NULL;
(*node) = new_node;
return;
}
new_node->next = (*node)->next;
(*node)->next = new_node;
new_node->prev = (*node);
if (new_node->next != NULL)
new_node->next->prev = new_node;
}
Struct:
typedef struct Polynomial {
int num;
int pow;
struct Polynomial* next;
struct Polynomial* prev;
}Polynomial;
When you create a new list, the first node's next pointer is unspecified. This could lead to undefined behaviour when inserting the second node, e.g. the 0xcdcdcdcd value you saw. Set it to null before returning:
if ((*node) == NULL) {
new_node->prev = new_node->next = NULL;
(*node) = new_node;
return;
}

Implement a doubly linked list in C

I want to insert a node at the beginning of linked list, whenever insertAtBeginning method is called. My code builds well, but i don't get the desired output.
I get the following output:
0------>NULL
The desired output is:
9------>8------>7------>6------>5------>4------>3------>2------>1------>0------>NULL
Following is my code:
#include<stdio.h>
#include<stdlib.h>
struct dll{
int data;
struct dll* previous;
struct dll* next;
};
struct dll* insertAtBeginning(int a, struct dll* head){
if(head == NULL){
head->data = a;
head->previous = NULL;
head->next = NULL;
return head;
}
else{
struct dll *first;
first = (struct dll*) malloc( sizeof(struct dll));
first->data = a;
first->next = head;
head->previous = first;
first->previous = NULL;
head = first;
free(first);
return head;
}
}
void display_from_first(struct dll* head){
struct dll *temp;
temp = head;
printf("\nThe linked list contains: ");
while(temp != NULL) {
printf("%d------>",temp->data);
temp = temp->next;
}
printf("NULL\n");
free(temp);
}
int main(){
int i = 0;
struct dll *head1, *tail1;
head1 = (struct dll*) malloc( sizeof(struct dll));
head1->next = NULL;
head1->previous = NULL;
for(i=0; i<10; i++){
insertAtBeginning(i, head1);
}
display_from_first(head1);
return 0;
}
The code for a doubly linked list is much cleaner if you start with an empty list of two nodes as shown below.
That way you don't have to deal with special cases like if(head==NULL). There's always a node before and after the node that is being inserted (or deleted), so you just hook things up and you're done.
#include <stdio.h>
#include <stdlib.h>
typedef struct s_node Node;
struct s_node
{
Node *prev;
Node *next;
int data;
};
Node *insertAtBeginning( Node *head, int value )
{
// allocate memory for the new node
Node *node = malloc( sizeof(Node) );
if ( node == NULL )
return NULL;
// insert the node at the beginning of the list
Node *temp = head->next;
head->next = node;
temp->prev = node;
// fill in the fields of the node
node->prev = head;
node->next = temp;
node->data = value;
return node;
}
void showList( Node *head )
{
Node *node;
printf( "The list contains: " );
for ( node = head->next; node->next != NULL; node = node->next )
printf( "%d--->", node->data );
printf( "NULL\n" );
}
int main( void )
{
// create an empty list with two nodes
Node head = { NULL , NULL, 0 };
Node tail = { &head, NULL, 0 };
head.next = &tail;
// insert more nodes
for ( int i = 0; i < 10; i++ )
insertAtBeginning( &head, i );
// display the list
showList( &head );
}
There are mainly two problems here :
free(first) : This is not required as you wish to save the memory you just allocated, not delete it.
Your insertAtBeginning() function returns a pointer to head, so in main(), where you are calling this function change it to head1=insertAtBeginning(i, head1); This way your head is also saved.
Here's the code with the two edits :
http://ideone.com/nXwc8z
You have several mistakes here.
1) Your function insertAtBeginning returns pointer to changed list, but you do not update pointer to head of list in the main function.
2) You are freeing just allocated pointer to new node in the insertion function. You think that you are freeing pointer, but actually you say that this place in memory is not needed more and so your node can't be there.
You can't free(first); in insertAtBeginning().
code here.
And btw when you have empty list your display_from_first() prints The linked list contains: 0------>NULL because of
head1 = (struct dll*) malloc( sizeof(struct dll));
head1->next = NULL;
head1->previous = NULL;
in main(). Remove it from main to have correct output

Program keeps crashing when I destroy my linked list

For some reason, my code keeps crashing.
If anyone can help me find out why, I would be very thankful.
int destroy(struct node *p)
{
struct node * temp = p;
struct node * next;
while (temp->next != NULL)
{
next = temp->next;
free(temp);
temp = next;
}
p = NULL;
return 1;
}
You need to test temp for null-ness, not temp->next:
void destroy(struct node *p)
{
struct node *temp = p;
while (temp != NULL)
{
struct node *next = temp->next;
free(temp);
temp = next;
}
}
You also don't need to set p to null (it doesn't do anything useful). And returning a status is not a good idea. Your callers either have to test it (but will never see anything other than 1, so the test is pointless), or they have to ignore it (in which case, why bother to return it?). You could do without the variable temp, too:
void destroy(struct node *list)
{
while (list != NULL)
{
struct node *next = list->next;
free(list);
list = next;
}
}
If you really want to set the pointer to null, you have to change the notation:
void destroy(struct node **list)
{
struct node *node = *list;
while (node != NULL)
{
struct node *next = node->next;
free(node);
node = next;
}
*list = NULL;
}
and instead of:
struct node *root = ...;
...
destroy(root);
you would have to write:
struct node *root = ...;
...
destroy(&root);

inserting in singly linked list in ascending order

Say I have a singly linked list of elements in ascending order that looks like:
A->B->D->E
I want to insert C in between B and D.
I know how to point C to D, but I don't know how to point B to C since the linked list does not keep track of the prev node.
While you are scanning down the list of nodes you have to keep two pointers: one points to the current node that you are interested in, and the other points to the previous node.
A possible implementation follows:
struct node {
int value;
struct node *next;
};
void sorted_insert(struct node **head, struct node *element) {
// Is the linked list empty?
if (*head == NULL) {
element->next = NULL;
*head = element;
return;
}
// Should we insert at the head of the linked list
if (element->value < (*head)->value) {
element->next = *head;
*head = element;
return;
}
// Otherwise, find the last element that is smaller than this node
struct node *needle = *head;
while (true) {
if (needle->next == NULL)
break;
if (element->value < needle->next->value)
break;
needle = needle->next;
}
// Insert the element
element->next = needle->next;
needle->next = element;
return;
}
You don't need to point B to C, or to maintain a pointer to the previous element. One method is:
Step to node D
malloc() a new node
Copy the data and next member from node D to your new node
Copy the data for node C into the existing node D (which now becomes node C)
Point the next member of the old node D to the new node.
For instance, excluding the possibility of inserting at the head of the list:
void insert(struct node * head, const int data, const size_t before)
{
assert(before > 0);
struct node * node = head;
while ( before-- && node ) {
node = node->next;
}
if ( !node ) {
fprintf(stderr, "index out of range\n");
exit(EXIT_FAILURE);
}
struct node * new_node = malloc(sizeof *new_node);
if ( !new_node ) {
perror("couldn't allocate memory for node");
exit(EXIT_FAILURE);
}
new_node->data = node->data;
new_node->next = node->next;
node->next = new_node;
node->data = data;
}
Why not keep track of the 'previous' node as you iterate through the list? Please forgive any syntactic shortcomings, as I haven't compiled this, but this should give you the idea.
struct node {
char name[ 10 ];
struct node *next;
};
struct list {
struct node *head;
};
void insert_C(struct list *list) {
struct node *new_node = malloc(sizeof(struct node));
if( new_node == NULL ) {
/* Error handling */
}
strcpy(new_node->name, "C");
struct node *pnode;
struct node *prev_node = NULL;
for( pnode = list->head; pnode->next != null; pnode = pnode->next ) {
if( !strcmp(pnode->name, "D") ) {
if( prev_node == NULL ) {
/* The 'C' node is going to be the new head. */
new_node->next = list->head;
list->head = new_node;
}
else {
prev_node->next = new_node;
new_node->next = pnode;
}
break;
}
/* Remember this node for the next loop iteration! */
prev_node = pnode;
}
}

Resources