Freeing a strdup inside of a linked list - c

I have a linked list in which I am trying to free a char* which I used strdup for.
These are the methods I have for adding a node into my list:
node* add_first(char* name, node* head) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
char* c = strdup(name);
new_node->name = c;
new_node->next = head;
head = new_node;
return head;
}
node * add_last(char* name, node *head ) {
node* new_node;
node* current;
new_node = (node*)malloc(sizeof(node));
char* c = strdup(name);
new_node->name = c;
new_node->next = NULL;
if ( head == NULL ){
head = new_node;
}
else{
current = head;
while ( current->next ) current = current->next;
current->next = new_node;
}
return head;
}
For removing nodes:
int remove_last( node **head, char **ret )
{
int result = *head == NULL ? -1 : 0;
if ( result == 0 )
{
while( ( *head )->next != NULL ) head = &( *head )->next;
*ret = strdup( ( *head )->name );
free( *head );
*head = NULL;
}
return result;
}
And for cleaning up the list:
void deleteList(node** head){
node* current = *head;
node* next;
while(current != NULL){
next = current->next;
free(current->name);
free(current);
current = next;
}
*head = NULL;
}
But valgrind says I have "definitely lost" memory when it comes to the strdup I have in my add_last function (but oddly not my add_first function).
Why is it only happening for the add_last method and how can I free it properly? I thought I was freeing all the nodes and the names in my deleteList method?

You are needlessly duplicating the string from the last-node removal algorithm, and leaking the original string. That algorithm should find the last node in the list, pass ownership of the name pointer to the out-param, then delete the node without deleting the name (since the out-param now owns it).
I.e. You should be doing something along these lines (fair warning, not compile-tested, but you hopefully get the idea):
int remove_last( node **head, char **ret )
{
int res = -1;
if (*head)
{
// find last node in the list
while ((*head)->next)
head = &(*head)->next;
// out-param takes ownership
*ret =(*head)->name;
// free last node *without* freeing the string
free(*head);
*head = NULL;
// result indicates success
res = 0;
}
return res;
}

Related

Program throws heap use after free error when I perform certain test cases

When I run the program it works except when I delete a fairly large number such as 100 or above, whenever I input anything remotely as large as that number I get the Heap use after free error. The terminal is saying it is caused by line 53 in insert() which is this line, tail->next = newNode;. I know keeping head and tail pointers as global variables are not the best way to write it, but I will change it once I get this to work.
void insert(int nData) {
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node)+10);
newNode->data = nData;
newNode->next = NULL;
if(checkDuplicates(nData)==1){
return;
}else{
if(head == NULL){
head = newNode;
tail = newNode;
}
else {
tail->next = newNode;
tail = newNode;
}
}
}
void delete(int n){
if(head -> data==n){
struct Node *tempHead = head;
head= head -> next;
free(tempHead);
return;
} else{
struct Node *current = head;
struct Node *prev = NULL;
while(current!=NULL&&current->data!=n){
prev = current;
current = current -> next;
}
if(current==NULL) return;
prev->next = current->next;
free(current);
}
}
There is a possible case where tail->next = newNode would be executed on a freed node: that happens when earlier on you called delete and that deleted the tail node in the list. In that case your code does not adjust the value of tail.
So in delete change:
head = head -> next;
to:
head = head -> next;
if (head == NULL) {
tail == NULL;
}
And in the else block change:
prev->next = current->next;
free(current);
to:
prev->next = current->next;
if (tail == current) {
tail = prev;
}
free(current);
For starters the function insert can produce a memory leak because at first a memory is allocated
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node)+10);
(moreover it is unclear what the magic number 10 means in this expression sizeof(struct Node)+10). And then if the condition of the if statement
if(checkDuplicates(nData)==1){
return;
evaluates to logical true the function exits without freeing the allocated memory.
You need at first to check whether the value is not already present in the list and only after that to allocate memory for the new node.
As for the reason of the error then it seems the reason of the error is inside the function delete. The function does not reset the pointer tail when a single node is present in the list or when the node pointed to by the pointer tail is removed.
And apart from this even the first statement of the function
if(head -> data==n){
can invoke undefined behavior when the function is called for an empty list.
The function should be defined the following way
int delete( int n )
{
struct Node *current = head;
struct Node *prev = NULL;
while ( current != NULL && current->data != n )
{
prev = current;
current = current->next;
}
int success = current != NULL;
if ( success )
{
if ( prev == NULL )
{
head = head->next;
if ( head == NULL ) tail = NULL;
}
else
{
prev->next = current->next;
if ( prev->next == NULL ) tail = prev;
}
free( current );
}
return success;
}

Trying to remove last element from linked list and save it into a variable passed as parameter giving segfault

I have a linked list in which I am trying to remove the last element that is a char* and set the parameter I pass into that function to the char*. However, this either gives a segfault or prints out gibberish. Why is this happening?
Here is the linked list:
struct list_node {
char* name;
struct list_node* next;
};
typedef struct list_node node;
And here are its functions (I'm pretty sure the other functions work but not the remove_last):
int remove_last(node* head, char* ret) {
while(head) {
if (head->next->next == NULL) {
printf("here\n");
ret = strdup(head->next->name);
printf("%s\n", ret);
printf("there\n");
//free(head->next);
//head->next = NULL;
return 0;
}
head = head->next;
}
return -1;
}
void add_last(char* name, node* current) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
while(current) {
if (current->next == NULL) {
current->next = new_node;
new_node->name = name;
return;
}
current = current->next;
}
}
node* add_first(char* name, node* head) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
new_node->name = name;
new_node->next = head;
head = new_node;
return head;
}
Inside the function, when I printf ret, I get what I am supposed to get, but in my main when I try to print out the ret variable I pass into parameters:
char *temp = NULL;
remove_last(head, temp);
printf("%s\n", temp);
I get a segfault. I originally thought maybe it was because I was setting the nodes and freeing them, but I also used strdup (which I think copies it to a new location or smth like that?). I think it might also have something to do with how I am setting temp to null and then I am not setting assigning ret = name correctly in the function? Any advice?
For starters the function add_last is incorrect and can invoke undefined behavior.
Firstly it does not check whether the pointer current (I suppose it can be a pointer to the head node) is equal to NULL. In this case there is a memory leak because the newly created node is not appended to the list.
Secondly the function does not set the data member next of the newly created node to NULL
At least the function can be declared and defined the following way if to use your approach to implementing the function add_first
node * add_last(char* name, node *head ) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
new_node->name = name;
new_node->next = NULL;
if ( head == NULL )
{
head = new_node;
}
else
{
current = head;
while ( current->next ) current = current->next;
current->next = new_node;
}
return head;
}
As for the function remove_last then the function parameter ret is a local variable of the function that will not be alive after exiting the function. The function parameter must have the type char **. That is the corresponding argument must be passed by reference to the function.
And in any case the function is incorrect because it ignores a case when the list contains only one node. You need to pass the pointer to the head node also by reference.
The function can look the following way.
int remove_last( node **head, char **ret )
{
int result = *head == NULL ? -1 : 0;
if ( result == 0 )
{
while( ( *head )->next != NULL ) head = &( *head )->next;
*ret = strdup( ( *head )->name );
free( *head );
*head = NULL;
}
return result;
}

Appending a new node to linked list

Basically what title says, Im trying to append (add to end of my list). my BuildList function takes in a size parameter that determines how many nodes the list will have. my problem is with my append function. So if I have 5 as my head, how do I fix my append function so that random numbers will be added after 5?
typedef struct Node
{
int value;
struct Node* next;
} Node;
Node *createNode( int num )
{
Node *ptr;
ptr = (Node *) malloc( sizeof( Node ) );
ptr->value = num;
ptr->next = NULL;
return ptr;
}
Node* append (Node* head, Node* newNode)
{
if (head == NULL)
return newNode;
while (head -> next != NULL);
head -> next = newNode;
return head;
}
Node* buildList (int size)
{
Node* newNode = (Node*) malloc (sizeof(Node));
Node* head = NULL;
for (int i = 0; i < size; i++)
{
Node* newNode = createNode (rand () % 10);
head = append (head, newNode);
}
return head;
}
Well, the most glaring issue is this
while (head -> next != NULL);
I believe you meant to write something like this
Node *tmp = head;
while (tmp -> next != NULL) {
tmp = tmp->next;
}
tmp->next = newNode;
You don't want to modify head here since you return it later in the function. If you didn't use tmp, head would always point to the penultimate node in the list.
You just need to modify your while, why do you have an empty instruction there? If the head is not NULL then you will never exit:
while (head -> next != NULL)
{
head = head -> next;
}

Faulty memory maniplation w / linked lists

The last printf call (printf("%d\n", current->val);) will not be executed. After the 1st printf functions' results appear, I am given the error "program.exe has stopped working". I would appreciate some help.
#include <stdio.h>
typedef struct node
{
int val;
struct node * next;
} node_t;
void print_list(node_t * head);
void main()
{
node_t * head = NULL;
head = malloc(sizeof(node_t));
if (head == NULL)
return 1;
head->val = 3;
head->next = malloc(sizeof(node_t));
head->next->val = 2;
head->next->next = malloc(sizeof(node_t));
head->next->next->val = 3;
head->next->next->next = malloc(sizeof(node_t));
head->next->next->next->val = 18;
print_list(head);
head->next->next->next->next = malloc(sizeof(node_t));
head->next->next->next->next->val = 5556;
head->next->next->next->next->next = NULL;
node_t * current = head;
while (current->next != NULL)
{
current = current->next;
}
current->next = malloc(sizeof(node_t));
current->next->val = 32;
current->next->next = NULL;
printf("%d", current->next->val);
system("pause");
}
void print_list(node_t * head) {
node_t * current = head;
while (current != NULL) {
printf("%d\n", current->val);
current = current->next;
}
}
This won't go well:
head->next->next->next = malloc(sizeof(node_t));
head->next->next->next->val = 18;
print_list(head);
You never initialized head->next->next->next->next to NULL. Instead, use calloc, or explicitly set the value to NULL. Even better, write a function to create a new node so you never forget to initialize. Even better still, write a function to insert a node.
What about something like this:
node_t * create_node( int val ) {
node_t *node = malloc(sizeof(*node));
if( node ) {
node->val = val;
node->next = NULL;
}
return node;
}
node_t * insert_value( node_t *list, int value ) {
node_t *new_node = create_node( value );
if( !new_node ) {
return list;
} else if( list ) {
new_node->next = list->next;
list->next = new_node;
}
return new_node;
}
Then you can:
node_t *head = insert_value( NULL, 3 );
node_t *tail = head;
tail = insert_value( tail, 2 );
tail = insert_value( tail, 3 );
tail = insert_value( tail, 18 );
print_list( head );
A common approach for lists is to use a dummy head node, which you never print out. It only contains a next pointer which is the start of your list and you ignore the value. If you did this, then you could also use that insert_value function to insert a value before the first element of the list. You also get the benefit that head always manages your entire list and you never have to worry about it changing.

Linked list in C – methods

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

Resources