there! I've been working on data structures & algorithms. One thing that has been bothering me a lot, is linked list.
I've check a lot of linked list code samples, but one thing that I have noticed in every single one of them, is how they create node structs & make the user link them together.
So I started searching ways of creating linked lists in different languages & porting them to C. I found a tutorial that worked with Java. After porting the code, insertion was working fine, but removing gives me seg faults.
I think it's working this way, since in Java, we have garbage collectors & the fact that after malloc() you need to free() the memory, but no matter, how much I thought about it, I couldn't wrap my head, to where I should put free(). So the struct is memory unsafe as well.
So my real problem is were I should use free() in this code(maybe the seg fault is for a completely different reason, who knows).
#include <stdlib.h>
struct node {int value; struct node* next;};
typedef struct {struct node* first; struct node* last;} linkedList;
void initializeLinkedList(linkedList* list)
{
list->first = list->last = NULL;
}
// O(1)
void addLastLinkedList(linkedList* list, int element)
{
// create a new node
struct node* insertionNode = malloc(sizeof(struct node));
insertionNode->value = element;
// check if the linked list is empty
if (list->first==NULL) list->first = list->last = insertionNode;
else
{
// make last node point to this node
list->last->next = insertionNode;
// make the last node, this node
list->last = insertionNode;
}
}
// O(1)
void addFirstLinkedList(linkedList* list, int element)
{
// create a new node
struct node* insertionNode = malloc(sizeof(struct node));
insertionNode->value = element;
// check if the linked list is empty
if (list->first==NULL) list->first = list->last = insertionNode;
else
{
// make node point to the first node
insertionNode->next = list->first;
// make it the first node
list->first = insertionNode;
}
}
// O(n)
int removeLastLinkedList(linkedList* list)
{
// check if the linked list is empty
if (list->first==NULL) return -1;
// check if the linked list only has a single item
if (list->first==list->last) list->first = list->last = NULL;
// get the last to second node
struct node* currentNode = list->first;
while (currentNode != NULL)
{
if (currentNode->next == list->last) break;
currentNode = currentNode->next;
}
// set the last node to the second to last node
list->last = currentNode;
list->last->next = NULL;
}
// O(1)
int removeFirstLinkedList(linkedList* list)
{
// check if the linked list is empty
if (list->first==NULL) return -1;
// check if the linked list only has a single item
if (list->first==list->last) list->first = list->last = NULL;
// get the second node
struct node* secondNode = list->first->next;
// remove the pointer of first node
list->first->next = NULL;
// make the second node, the first node
list->first = secondNode;
return 0;
}
Within the both functions addLastLinkedList and addFirstLinkedList you forgot to initialize to NULL the data member next of the new node. Add statement
insertionNode->next = NULL;
The function removeLastLinkedList invokes undefined behavior when the list contains only one node because after this if statement
if (list->first==list->last) list->first = list->last = NULL;
the control is passed further to this code snippet
struct node* currentNode = list->first;
//...
// set the last node to the second to last node
list->last = currentNode;
list->last->next = NULL;
where there is used the null pointer list->last to access data member next.
Also the function produces a memory leak because the removed node is not freed.
And the function returns nothing if the list was not empty.
At least rewrite the function like
int removeLastLinkedList(linkedList* list)
{
// check if the linked list is empty
if (list->first==NULL) return -1;
// check if the linked list only has a single item
if ( list->first == list->last )
{
free( list->first );
list->first = list->last = NULL;
}
else
{
// get the last to second node
struct node *currentNode = list->first;
while ( currentNode->next != list->last )
{
currentNode = currentNode->next;
}
free( currentNode->next );
// set the last node to the second to last node
list->last = currentNode;
list->last->next = NULL;
}
return 0;
}
Similar problems exist in the function removeFirstLinkedList
Rewrite the function like
int removeFirstLinkedList(linkedList* list)
{
// check if the linked list is empty
if (list->first==NULL) return -1;
// check if the linked list only has a single item
if ( list->first == list->last )
{
free( list->first );
list->first = list->last = NULL;
}
else
{
// get the second node
struct node *secondNode = list->first->next;
free( list->first );
// make the second node, the first node
list->first = secondNode;
}
return 0;
}
Here is ANOTHER brief example of the four non-trivial functions in your OP.
While it is apparent that the code (and comments) is struggling to get things happening correctly, the long variable and function names make it difficult to read. This "less verbose" version may bring some clarity to seeing/thinking about what operations are being performed and in what sequence.
The first two functions below deal with the "head" of the linked list. They are somewhat easier and "set the mood" before reading the second two functions that deal with the "tail".
// Use typedefs and short conventional names
typedef struct node {
int value;
struct node *next;
} node_t;
typedef struct {
node_t *first;
node_t *last;
} ll_t;
// Short names!
void LLprepend( ll_t *p, int element ) {
node_t *nn = calloc( 1, sizeof *nn ); // omitting verification
nn->value = element;
nn->next = p->first; // Always
p->first = nn;
if( p->last == NULL ) // first node on list
p->last = nn;
}
int LLtrimhead( ll_t *p ) {
if( p->first == NULL )
return -1;
node_t *pDel = p->first; // target node being removed
p->first = p->first->next; // advance ptr (may be NULL)
free( pDel ); // <<===
if( p->first == NULL ) // list now empty??
p->last = NULL;
return 0;
}
// Short names!
void LLappend( ll_t *p, int element ) {
// calloc initializes all bytes to NULL
node_t *nn = calloc( 1, sizeof *nn ); // omitting verification
nn->value = element; // store data
if( p->last == NULL )
p->first = p->last = nn; // first node on list
else
p->last = p->last->next = nn; // appended & adjusted
}
int LLtrimtail( ll_t *p ) {
if( p->last == NULL )
return -1;
node_t *pPen = p->first; // "Penultimate" node
// traverse to penultimate node
while( pPen->next && pPen->next->next )
pPen = pPen->next;
if( pPen == p->first ) { // Only 1 node on list
free( pPen ); // <<===
p->first = p->last = NULL;
return 0;
}
free( pPen->next ); // <<===
p->last = pPen; // last becomes what was 2nd last
return 0;
}
Related
I need a little help removing unique characters in a doubly linked list in C. So here's the logic I tried implementing: I counted the occurrence of each character in the doubly linked list. If it's occurrence is 1 time, then it is unique element and needs to be deleted. I'll be repeating the process for all elements. But my code in remove_unique_dll() function isn't working properly, please help me fix it. Here's my code-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char data;
struct node *next;
struct node *prev;
};
struct node *head, *tail = NULL; //Represent the head and tail of the doubly linked list
int len;
void addNode(char data)
{
struct node *newNode = (struct node*) malloc(sizeof(struct node)); //Create new node
newNode->data = data;
if (head == NULL)
{ //If dll is empty
head = tail = newNode; //Both head and tail will point to newNode
head->prev = NULL; //head's previous will point to NULL
tail->next = NULL; //tail's next will point to NULL, as it is the last node of the list
}
else
{
tail->next = newNode; //newNode will be added after tail such that tail's next points to newNode
newNode->prev = tail; //newNode's previous will point to tail
tail = newNode; //newNode will become new tail
tail->next = NULL; //As it is last node, tail's next will point to NULL
}
}
void remove_unique_dll()
{
struct node *current = head;
struct node *next;
struct node *prev;
int cnt;
while (current != NULL)
{
next = current->next;
cnt = 1;
//printf("!%c ",next->data);
while (next != NULL)
{
if (next->data == current->data)
{
cnt += 1;
next = next->next;
}
else
next = next->next;
//printf("#%c %d %c\n",next->data,cnt,current->data);
}
if (cnt == 1)
{
prev = current->prev;
//printf("#%c %d",prev->data,cnt);
if (prev == NULL)
{
head = next;
}
else
{
prev->next = next;
}
if (next == NULL)
{
tail = prev;
}
else
{
next->prev = prev;
}
}
current = current->next;
//printf("#%c ",current->data);
}
head = current;
}
void display()
{
struct node *current = head; //head the global one
while (current != NULL)
{
printf("%c<->", current->data); //Prints each node by incrementing pointer.
current = current->next;
}
printf("NULL\n");
}
int main()
{
char s[100];
int i;
printf("Enter string: ");
scanf("%s", s);
len = strlen(s);
for (i = 0; i < len; i++)
{
addNode(s[i]);
}
printf("Doubly linked list: \n");
display();
remove_unique_dll();
printf("Doubly linked list after removing unique elements: \n");
display();
return 0;
}
The output is like this-
If you uncomment the printf() statements inside remove_unique_dll() you'll notice that no code below inner while loop is being executed after inner while loop ends. What's the issue here and what's the solution?
Sample input- aacb
Expected output- a<->a<->NULL
Some issues:
You shouldn't assign head = current at the end, because by then current is NULL
The next you use in the deletion part is not the successor of current, so this will make wrong links
As you progress through the list, every value is going to be regarded as unique at some point: when it is the last occurrence, you'll not find a duplicate anymore, as your logic only looks ahead, not backwards.
When you remove a node, you should free its memory.
Not a big issue, but there is no reason to really count the number of duplicates. Once you find the first duplicate, there is no reason to look for another.
You should really isolate the different steps of the algorithm in separate functions, so you can debug and test each of those features separately and also better understand your code.
Also, to check for duplicates, you might want to use the following fact: if the first occurrence of a value in a list is the same node as the last occurrence of that value, then you know it is unique. As your list is doubly linked, you can use a backwards traversal to find the last occurrence (and a forward traversal to find the first occurrence).
Here is some suggested code:
struct node* findFirstNode(char data) {
struct node *current = head;
while (current != NULL && current->data != data) {
current = current->next;
}
return current;
}
struct node* findLastNode(char data) {
struct node *current = tail;
while (current != NULL && current->data != data) {
current = current->prev;
}
return current;
}
void removeNode(struct node *current) {
if (current->prev == NULL) {
head = current->next;
} else {
current->prev->next = current->next;
}
if (current->next == NULL) {
tail = current->prev;
} else {
current->next->prev = current->prev;
}
free(current);
}
void remove_unique_dll() {
struct node *current = head;
struct node *next;
while (current != NULL)
{
next = current->next;
if (findFirstNode(current->data) == findLastNode(current->data)) {
removeNode(current);
}
current = next;
}
}
You have at least three errors.
After counting the number of occurrences of an item, you use next in several places. However, next has been used to iterate through the list. It was moved to the end and is now a null pointer. You can either reset it with next = current->next; or you can change the places that use next to current->next.
At the end of remove_unique_dll, you have head=current;. There is no reason to update head at this point. Whenever the first node was removed from the list, earlier code in remove_unique_dll updated head. So it is already updated. Delete the line head=current;.
That will leave code that deletes all but one occurrence of each item. However, based on your sample output, you want to leave multiple occurrences of items for which there are multiple occurrences. For that, you need to rethink your logic in remove_unique_dll about deciding which nodes to delete. When it sees the first a, it scans the remainder of the list and sees the second, so it does not delete the first a. When it sees the second a, it scans the remainder of the list and does not see a duplicate, so it deletes the second a. You need to change that.
Let's consider your code step by step.
It seems you think that in this declaration
struct node *head, *tail = NULL; //Represent the head and tail of the doubly linked list
the both pointers head and tail are explicitly initialized by NULL. Actually only the pointer tail is explicitly initialized by NULL. The pointer head is initialized implicitly as a null pointer only due to placing the declaration in file scope. It to place such a declaration in a block scope then the pointer head will be uninitialized.
Instead you should write
struct node *head = NULL, *tail = NULL; //Represent the head and tail of the doubly linked list
Also it is a very bad approach when the functions depend on these global variables. In this case you will be unable to have more than one list in a program.
Also the declaration of the variable len that is used only in main as a global variable
int len;
also a bad idea. And moreover this declaration is redundant.
You need to define one more structure that will contain pointers head and tail as data members as for example
struct list
{
struct node *head;
struct node *tail;
};
The function addNode can invoke undefined behavior when a new node can not be allocated
void addNode(char data)
{
struct node *newNode = (struct node*) malloc(sizeof(struct node)); //Create new node
//...
You should check whether a node is allocated successfully and only in this case change its data members. And you should report the caller whether a node is created or not.
So the function should return an integer that will report an success or failure.
In the function remove_unique_dll after this while loop
while (next != NULL)
{
if (next->data == current->data)
{
cnt += 1;
next = next->next;
}
else
next = next->next;
//printf("#%c %d %c\n",next->data,cnt,current->data);
}
if cnt is equal to 1
if (cnt == 1)
//..
then the pointer next is equal to NULL. And using the pointer next after that like
if (prev == NULL)
{
head = next;
}
else
{
prev->next = next;
}
is wrong.
Also you need to check whether there is a preceding node with the same value as the value of the current node. Otherwise you can remove a node that is not a unique because after it there are no nodes with the same value.
And this statement
head = current;
does not make sense because after the outer while loop
while (current != NULL)
the pointer current is equal to NULL.
Pay attention that the function will be more useful for users if it will return the number of removed unique elements.
Here is a demonstration program that shows how the list and the function remove_unique_dll can be defined.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char data;
struct node *next;
struct node *prev;
};
struct list
{
struct node *head;
struct node *tail;
};
int addNode( struct list *list, char data )
{
struct node *node = malloc( sizeof( *node ) );
int success = node != NULL;
if (success)
{
node->data = data;
node->next = NULL;
node->prev = list->tail;
if (list->head == NULL)
{
list->head = node;
}
else
{
list->tail->next = node;
}
list->tail = node;
}
return success;
}
size_t remove_unique_dll( struct list *list )
{
size_t removed = 0;
for ( struct node *current = list->head; current != NULL; )
{
struct node *prev = current->prev;
while (prev != NULL && prev->data != current->data)
{
prev = prev->prev;
}
if (prev == NULL)
{
// there is no preceding node with the same value
// so the current node is possibly unique
struct node *next = current->next;
while (next != NULL && next->data != current->data)
{
next = next->next;
}
if (next == NULL)
{
// the current node is indeed unique
struct node *to_delete = current;
if (current->prev != NULL)
{
current->prev->next = current->next;
}
else
{
list->head = current->next;
}
if (current->next != NULL)
{
current->next->prev = current->prev;
}
else
{
list->tail = current->prev;
}
current = current->next;
free( to_delete );
++removed;
}
else
{
current = current->next;
}
}
else
{
current = current->next;
}
}
return removed;
}
void display( const struct list *list )
{
for (const node *current = list->head; current != NULL; current = current->next)
{
printf( "%c<->", current->data );
}
puts( "null" );
}
int main()
{
struct list list = { .head = NULL, .tail = NULL };
const char *s = "aabc";
for (const char *p = s; *p != '\0'; ++p)
{
addNode( &list, *p );
}
printf( "Doubly linked list:\n" );
display( &list );
size_t removed = remove_unique_dll( &list );
printf( "There are removed %zu unique value(s) in the list.\n", removed );
printf( "Doubly linked list after removing unique elements:\n" );
display( &list );
}
The program output is
Doubly linked list:
a<->a<->b<->c<->null
There are removed 2 unique value(s) in the list.
Doubly linked list after removing unique elements:
a<->a<->null
You will need at least to write one more function that will free all the allocated memory when the list will not be required any more.
this is my funtion, if i use this function to delete the first node it will delete all the rest of node->next node, anyone can help me?
struct node* popCH(char ex[]){
int hash_index = Hash(ex);
node* temp = head[hash_index];
if(temp==NULL){
return NULL;
}
while(temp->next!=NULL && strcmp(temp->next->ex,ex)!=0){
temp=temp->next;
}
if(temp->next==NULL && strcmp(temp->ex,ex)==0){
free(temp);
temp=NULL;
head[hash_index]=NULL;
}
else if(temp->next!=NULL && strcmp(temp->ex,ex)==0){
node* temp2 = temp;
temp = temp->next;
temp = head[hash_index]->next;
free(temp2);
temp2=NULL;
}
}
You should use a pointer to a pointer to a node so that the node can be deleted and the pointer to the node is updated to point at the next node regardless of the pointer is the head pointer of a pointer in a node.
struct data* popCH(char ex[]){
int hash_index = Hash(ex);
node** temp = &head[hash_index];
if(*temp==NULL){
return NULL;
}
while(*temp!=NULL && strcmp((*temp)->ex,ex)!=0){
temp=&(*temp)->next;
}
if(*temp!=NULL){
node *temp2 = (*temp)->next;
free(*temp);
*temp=temp2;
}
/* return something not to let caller invoke undefined behavior by using the return value */
return NULL;
}
One approach is with a pointer-to-pointer, which is already covered by an excellent answer.
Another one is simply to keep track of the pointer to the previous node, which is null when there is no previous node:
node *prev = NULL;
while (temp != NULL && strcmp(temp->ex, ex) != 0) {
prev = temp;
temp = temp->next;
}
if (temp != NULL) { // found matching node
// retain data pointer to return before freeing
struct data *data = temp->data;
// two deletion cases: middle of chain or front
if (prev)
prev->next = temp->next;
else
head[hash_index] = temp->next;
free(temp);
return data;
}
// not found
return NULL;
We can avoid having two deletion cases if we give the chain a surrogate parent. That is to say, we add a dummy node to the front of the list which serves as the previous node for the first node:
struct data* popCH(const char *ex){
int hash_index = Hash(ex);
// transfer chain into fake extra previous node:
struct node parent = { .next = head[hash_index] };
node *prev = &parent;
node *temp = prev->next;
while (temp != NULL && strcmp(temp->ex, ex) != 0) {
prev = temp;
temp = temp->next;
}
if (temp != NULL) { // found matching node
// retain data pointer to return before freeing
struct data *data = temp->data;
// splice temp out of list by getting
// the previous node to skip it
prev->next = temp->next;
// transfer updated chain back into table:
head[hash_index] = parent.next;
free(temp);
return data;
}
// not found
return NULL;
}
We have eliminated some tests from the code, but there are some unnecessary memory writes. Before returning, we assign to head[hash_index] whether or not that is actually necessary; it is only necessary of parent.next has changed. Moreover, we have allocated an entire node local variable and initialized all of its fields, even though we only ever access `. We can avoid doing that by avoiding the initializer:
int hash_index = Hash(ex);
// transfer chain into fake extra previous node:
struct node parent;
node *prev = &parent;
node *temp = head[hash_index];
// initialize just parent.next member by assignment;
// other members are left uninitialized.
parent.next = temp;
// rest of code ...
Why the program doesn't add after pops the last item? There is something missing with pointers on these 2 functions
First, I have defined a Node struct and named it node. Then created 2 pointers to point the first and the last one.
typedef struct Node {
int data;
struct Node* next;
} node;
node* first = NULL, * last = NULL;
Here, Pop function. I have created a walk pointer. If the first node is going to be deleted, first will be pointed to NULL. Else, it will iterate to the last node and deallocates it.
void Pop() {
if (first == NULL) {
printf("\n\nLIST IS EMPTY\n\n");
}
else if (first->next == NULL) {
node* temp = (node*)malloc(sizeof(node));
temp = first;
free(temp);
first = NULL;
}
else {
node* walk = first;
while (walk->next->next != NULL) {
walk = walk->next;
}
free(walk->next);
walk->next = NULL;
}
}
Push function will create a node and add it to the list. If the list is empty, it will be pointed out by first and last, else it will be connected to the last node then it will become the last node.
void Push(int data) {
node* temp = (node*)malloc(sizeof(node));
temp->data = data;
if (first == NULL) {
first = temp;
temp->next = NULL;
last = first;
}
else {
last->next = temp;
last = last->next;
last->next = NULL;
}
}
In Pop() you never update last, so it still points to the deleted node. When you push again you are linking the deleted node to the new node.
void Pop() {
node* walk = first;
node* deleted = NULL;
while (walk->next->next != NULL) {
walk = walk->next;
}
deleted = walk->next;
free(deleted);
walk->next = NULL;
last = walk; // <-- add this
}
Also, Pop() will fail (and probably crash) if you only have one node in the list because walk->next->next will dereference a NULL pointer.
I need to implement a function that reverses a linked list but i don't know how to return a newly formed linked list as a result.
typedef struct node_t* Node;
struct node_t {
int n;
Node next;
};
// create a new node with value n
Node nodeCreate(int n)
{
Node node = malloc(sizeof(*node));
if (node == NULL) return NULL;
node->n = n;
node->next = NULL;
return node;
}
// reversing the linked list
Node reverseList(Node list)
{
if (list == NULL) return NULL;
Node current, prev, next;
current = rev_head;
prev = NULL;
while( current != NULL)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
}
list = prev;
return list;
}
This is the code I have written so far.
How do I insert this reverse linked list into a different new linked list within the function reverseList it self?
You are juggling around your nodes on the original list, and modifying then in place - which means you have only one list, and modify its nodes.
If that is what you want, your code might work (as soon as you fix things like the rev_head variable that appears from nowhere) - and the new head of the list, which is on your prev variable: which means your code should just work.
(It is important that the typedef don't hide the pointer though, I'd suggest changing that.)
What you seem not to have understood quite well is that for this kind o structure, any node works as the head of a list - there is no "list" type, just "node" types - and if you happen to pick any node in the middle of a list, that will just represent a partial list, starting from that node as well. So when you change your previous "last" node to point to its previous "antecessor" as its "next", that is it: that node is now the head.
(The exception to this "node == list" equality is while the reversing algorithm is running - at that point you have nodes that point in one direction and nodes that point in another, and the extra "next" and "prev" variables provide the needed information to fix things. If this was production code, this part of the code would have to be protected in a thread-lock)
Otherwise, if you want to produce a reversed copy of the list, you will have to copy the old nodes along the way, and just fix where they are pointing.
#include <stdlib.h>
#include <string.h>
typedef struct node_t Node;
struct node_t {
int n;
Node next;
};
// create a new node with value n
Node *nodeCreate(int n) {
Node *node = malloc(sizeof(*node));
if (node == NULL) return NULL;
node->n = n;
node->next = NULL;
return node;
}
void nodeCopy(Node *node_dst, Node *node_src) {
if (node_src == NULL || node_dst == NULL || abs(node_dst - node_src) < sizeof(Node)) {
return
}
memcpy(node_dst, node_src, sizeof(Node));
}
// reversing the linked list
Node *reverseList(Node *list) {
Node *new_list, *prev;
if (list == NULL) return NULL;
new_list = nodeCreate(0);
nodeCopy(new_list, list);
new_list->next=NULL;
prev = new_list;
while(list->next != NULL) {
list = list->next;
new_list = nodeCreate(0);
nodeCopy(new_list, list);
new_list->next=prev;
prev = new_list;
}
return new_list;
}
You can simply create a new node for every node in the original list and set the link in opposite order. Code could be:
Node createReversedList(Node node) {
Node result = NULL;
while (node != NULL) {
Node n = nodeCreate(node->n);
n->next = result;
result = n;
node = node->next;
}
return result;
}
There is a simple way in which you can reverse linked list
You can simply create a new linked list and add each node at the start of linked list which will create same linked list in reverse order
Node* revList(Node *start){
Node *startRev=NULL,*temp,*newNode;
temp = start;
while (temp->next!=NULL){
newNode = (Node *) malloc (sizeof(Node));
//Code for copying data from temp to new node
if(startRev == NULL){
startRev = newNode;
}
else {
newNode->next = startRev;
startRev = newNode;
}
temp = temp->next;
}
return startRev;
}
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;
}