C - Linked Lists - Deleting Head - Segmentation Fault - c

I am working on a problem for a class and we're learning linked lists in C. I was given a section of code to complete, specifically the delete a node section and I'm having a problem deleting head. Every time I try to delete head I receive a segmentation fault. Can someone tell me what I'm doing wrong?
EDIT2
My teacher wrote everything but the lookup and delete functions.
I have fixed the glaring errors pointed out by the gentleman from Moscow and Mr. Petriuc, however the code still doesn't run. It does compile, but there is still a problem in head.
Here is the full code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "linkedList.h"
// keep an unsorted array of char *'s, strings.
/*
Create an empty node, return 0 if fail, 1 if succeed
*/
struct node * createNode() {
struct node *p = (struct node *) malloc(sizeof(struct node));
if (p == NULL) return 0;
p->prev = p->next = NULL;
p->data = NULL;
}
/*
Lookup string in the list, return pointer to node of first occurence, NULL if not found.
*/
struct node * lookup(struct node *head, char *s) {
struct node *p;
for(p=head; p!=NULL; p=p->next){
if(strcmp(s,p->data)==0){
return p;
}
// just like print, but check if strcmp(s, p->data) == 0, and if so then return p
}
return NULL;
}
/*
Insert new string into the linked list, return 1 if success, 0 if fail.
*/
int insert(struct node **head, char *newS, int insertDuplicate) {
struct node *p = lookup(*head, newS);
if (p == NULL || insertDuplicate) {
// create a new node, put it at the front.
p = createNode();
if (p == NULL) return 0;
// put the string in the new node
p->data = (char *) malloc(sizeof(char) * (1 + strlen(newS)));
if (p->data == NULL) return 0;
strcpy(p->data, newS);
// note: make changes and use old head before setting the new head...
p->next = *head; // next of new head is previous head
if (*head != NULL)
(*head)->prev = p; // previous of old head is new head
*head = p; // set the new head
}
return 1;
}
/*
Remove string from list if found, return 1 if found and deleted, 0 o/w.
*/
int delete(struct node **head, char *s) {
struct node *p,*pr,*ne;
// first do a lookup for string s, call lookup.
p=lookup(*head, s);
if(p==*head){
*head = p->next;
free(p);
return 1;
}
if(p!=NULL){
printf("%s",p);
pr = p->prev;
ne = p->next;
free(p->data);
free(p);
if(pr==NULL||ne==NULL){
return 0;
}
pr->next=ne;
ne->prev=pr;
// if lookup returns NULL, done, return 0.
// if lookup returns p, not NULL,
// pr = p->prev, ne = p->next
// set pr->next to ne, ne->prev to pr
// but what if pr or ne is NULL
// and note that we need node **head because if delete head,
// need to update head pointer back in calling function, in
// here if you want head probably do *head. like in insert.
// also, before the pointer to the one you're deleting is gone,
// free p->data and p.
return 1;
}
return 0;
}
void print(struct node *head) {
struct node *p;
for(p = head; p != NULL ; p = p->next) {
printf("%s\n", p->data);
}
}

You are doing
p->next = *head;
But p is not assigned anywhere.

Your function does not make sense. You call the function lookup three times.
Moreover you use pointers that were not initialized like for example
p->next = *head;
or
printf("%s",p);
pr = p->prev;
ne = p->next;
The function can be written the following way
int delete( struct node **head, char *s )
{
int success;
struct node *target = lookup( *head, s );
if ( ( success = target != NULL ) )
{
if ( target->prev != NULL )
{
target->prev->next = target->next;
}
else
{
*head = target->next;
}
if ( target->next != NULL )
{
target->next->prev = target->prev );
}
free( target );
}
return success;
}
Take into account that the second parameter of the function and the corresponding parameter of the function lookup should be declared with qualifier const
int delete( struct node **head, const char *s ) ;
^^^^^
struct node * lookup( struct node *head, const char *s );
^^^^^^

Simplified delete() function. I inlined lookup() because the function as it is is worthless (you need a pointer to pointer, not a pointer to act upon)
/*
Remove string from list if found, return 1 if found and deleted, 0 o/w.
*/
int delete(struct node **head, char *s) {
struct node *tmp;
// first do a lookup for string s, no need to call call lookup.
for( ;*head; head = &(*head)->next ){
if (!strcmp( (*head)->data, s)) break;
}
if (!*head) return 0; // not found
tmp = *head
*head = tmp->next
free(tmp);
return 1;
}

Related

Copy a pointer to a struct in a function (linked list)

I want to craete a single linked list without gloabal variables. I initialized the first element with NULL and then wanted to copy the first element node to list_. It is copied in the function but the side effect isn´t working. In my main-function the value is still NULL.
If I return the struct in the add_element()function all works fine but, is it possible that l gets the value of node without changing the functions structure and the struct itself?
#include <stdio.h>
#include <stdlib.h>
struct list {
int value;
struct list *next;
};
struct list *initialize(void)
{
struct list * l = NULL;
return l;
}
int add_element(struct list *list_, void *v)
{
struct list *node = malloc(sizeof(struct list));
node->value = *((int*)v);
node->next = NULL;
if(list_ == NULL)
{
list_ = node;
printf("list_->value = %d\n", list_->value); // correct copy
return 0;
}
//TODO if not first then add at end..
return 0;
}
int main()
{
struct list *l = initialize(); // l = NULL
int i = 10;
add_element(l,&i);
if(l == NULL) printf("l == NULL!\n");
printf("l->value = %d\n", l->value); // does not work, l is NULL
return 0;
}
kaylum's comment points you in the right direction.
When you pass a pointer in C, the pointer's value is copied to the stack, and this copy is the value that the add_element() function is referring to. When you alter the pointer's value, you are modifying the copy placed on the stack, not the original pointer.
If you want to alter the original pointer (as if it was passed by reference and not by value) you need to use a double pointer.
Try this variant:
int add_element(struct list **list_, void *v)
{
struct list *node = malloc(sizeof(struct list));
node->value = *((int*)v);
node->next = NULL;
if(*list_ == NULL) // dereferencing the double pointer will access the original pointer
{
*list_ = node; // this will change the original pointer
printf("(*list_)->value = %d\n", (*list_)->value); // correct copy
return 0;
}
//TODO if not first then add at end..
return 0;
}
int main()
{
struct list *l = initialize(); // l = NULL
int i = 10;
add_element(&l,&i); // notice you are now passing the address of l instead of its value
if(l == NULL) printf("l == NULL!\n");
printf("l->value = %d\n", l->value); //should work now
return 0;
}
For starters the function initialize
struct list *initialize(void)
{
struct list * l = NULL;
return l;
}
does not make great sense. You can just write in main
struct list *l = NULL;
Or the function initialize can look like
inline struct list *initialize(void)
{
return NULL;
}
The function add_element deals with a copy of the passed list.
int add_element(struct list *list_, void *v);
So any changes of the copy do not influence on the original list. Also it is unclear why the second parameter has the type void * instead of the type int.
You have to pass the list by reference to the function.
The function can look the following way
int add_element( struct list **head, int value )
{
struct list *node = malloc( sizeof( struct list ) );
int success = node != NULL;
if ( success )
{
node->value = value;
node->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = node;
}
return success;
}
and called for example like
int i = 10;
if ( !add_element( &l, i ) )
{
puts( "Error: not enough memory." );
}
Here is a demonstrative program
#include <stdio.h>
#include <stdlib.h>
struct list
{
int value;
struct list *next;
};
static inline struct list * initialize( void )
{
return NULL;
}
int add_element( struct list **head, int value )
{
struct list *node = malloc( sizeof( struct list ) );
int success = node != NULL;
if ( success )
{
node->value = value;
node->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = node;
}
return success;
}
void output( struct list *head )
{
for ( ; head != NULL; head = head->next )
{
printf( "%d -> ", head->value );
}
puts( "NULL" );
}
int main(void)
{
struct list *head = initialize();
const int N = 10;
for ( int i = 0; i < N; i++ )
{
add_element( &head, i );
}
output( head );
return 0;
}
Its output is
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> NULL
Pay attention to that if a new node is appended to the tail of the list then it is better to define the list as a two-sided singly-linked list.

Global pointer (head of linked list) not updated properly

I have searched a lot, before I ask this, but I can't get this small piece of code to work.
I know that using a global pointer (or variable) is considered a bad practice (instead of passing by reference) but I am forced to use this practice sadly.
What I am trying to do is to make a linked list which consists of nodes (struct with some info), and after every insert() the list is dynamically expanded by one node (unless the element in question already exists, in that case the member name is overwritten).
The pointer next points to the next element in the list (that's where I assign the new node from malloc().
The program compiles correctly and executes with the following output:
retrieve returned: (NULL) at every printf() call
That's why I believe the global pointer (head of the list) is not updated properly
I am sorry for this naive question but I can't seem to find where the assignment/allocation goes wrong, Anyway thanks in advance for your assistance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct node{
char *id;
char *name;
struct node *next;
};
struct node* list; //list head
struct node* p; //pointer to list head
char *retrieve(char *id){
if(list == NULL)
return NULL; //list is empty, no element to return.
for(p = list; p != NULL; p = p->next)
if(strcmp(id, p->id) == 0)
return p->name;
return NULL;
}
void insert(char *id, char *name){
int exists = 0;
struct node* temp = NULL;
for(p = list; p != NULL; p = p->next){
if(strcmp(id, p->id) == 0){ //id already exists, overwrite with the new name.
free(p->name);
p->name = strdup(name);
exists = 1;
break;
}
}
if(exists) return;
//insert at the end of the list
temp = malloc(1 * sizeof(struct node));
if(temp == NULL){
printf("memory allocation failed\n");
return;
}
temp->id = strdup(id);
temp->name = strdup(name);
temp->next = NULL;
p = temp;
return;
}
int main(){
struct node* temp = NULL;
p = NULL;
list = NULL;
insert("145a","Jim");
insert("246b","Alice");
insert("322c","Mike");
printf("retrieve returned: %s\n\n", retrieve("145a"));
printf("retrieve returned: %s\n\n", retrieve("246b"));
printf("retrieve returned: %s\n\n", retrieve("322c"));
p = list;
while(p != NULL){ // node deletion starting from first to last element.
free(p->id);
free(p->name);
temp = p;
p = p->next;
free(temp);
}
return 0;
}
void insert(char *id, char *name)
{
struct node *temp = NULL, **pp;
/* Pointer to pointer points to the global */
for(pp = &list; *pp ; pp = &(*pp)->next){
if(strcmp(id, (*pp)->id) ) continue;
free((*pp)->name);
(*pp)->name = strdup(name);
return;
}
//insert at the end of the list
temp = malloc(sizeof *temp);
if(!temp ){
printf("memory allocation failed\n");
return;
}
temp->id = strdup(id);
temp->name = strdup(name);
temp->next = NULL;
*pp = temp;
return;
}
And you can even do without the *temp pointer:
void insert(char *id, char *name)
{
struct node **pp;
for(pp = &list; *pp ; pp = &(*pp)->next){
if(strcmp(id, (*pp)->id) ) continue;
free(p->name);
p->name = strdup(name);
return;
}
// pp now points to the terminal NULL pointer
*pp = malloc(sizeof **pp);
if(!*pp ){
printf("memory allocation failed\n");
return;
}
(*pp)->id = strdup(id);
(*pp)->name = strdup(name);
(*pp)->next = NULL;
return;
}
You never initialize list other than with NULL. In consequence,
char *retrieve(char *id){
if(list == NULL)
return NULL;
always returns NULL.

Node Insertion,Linked Lists

I have this code through which it inserts node at the starting.This code also contains a function which prints linked list and if empty it prints linked list is empty .
when i run this code , i have my ouput as Linked list is empty .
struct node {
int data;
node* next;
}* start = NULL;
void append(node* linkedlist, int data)
{
node* new_element = NULL;
new_element = (node*)malloc(sizeof(struct node));
new_element->data = data;
if (linkedlist == NULL) {
linkedlist = new_element;
new_element->next = NULL;
}
else {
new_element->next = (linkedlist)->next;
(linkedlist)->next = new_element;
}
}
int main()
{
append(start, 4);
append(start, 5);
printList(start);
}
Update:
void printList(node* linkedlist)
{
node* ptr = linkedlist;
if (linkedlist == NULL) {
printf("Linked list is empty");
exit(0);
}
else {
while (ptr != NULL) {
cout << ptr->data;
ptr = ptr->next;
}
}
}
What could i be possibly doing wrong? What should i change in order to make it work?
Your problem is here linkedlist = new_element; When passing arguments to a function they are passed by value. Even when you pass a pointer you are actually passing a copy of that pointer(you can verify by printing the address of linkedlist inside the function and outside the function). The statement linkedlist = new_element; assigns the new_element to a copy. Once the function return you end up with nothing (and a memory leak). Remember when you need to change a pointer itself you must use a double pointer **
You put in the value of start which points to null. Then you copy it to another pointer linkedlist and set it to a new value. After the function start is still pointing to null though because you never changed the value of start.
You could try to change your declaration to
void append(node *&linkedlist,int data)
If you are using a c++ compiler.
else
void append(node **linkedlist,int data)
...
append(&start,4);
if you are using C
For starters the code you showed is not valid C code. You are using C++ elements. So the program will not even compile as a C program.
As the node start that is initially set to NULL can be changed by the function you have to pass it by reference. Otherwise the function parameter linkedlist is a local variable of the function that will be changed in the function and at last will be destroyed after exiting the function. So the original pointer start itself will not be changed.
Also this else code block
else {
new_element->next = (linkedlist)->next;
(linkedlist)->next = new_element;
}
is wrong. This code block does not insert a new node in the beginning of the list. It inserts a new node after the first already existent node.
Take into account that the function name append is not suitable for inserting a node in the beginning of the list. It would be better to name it like insert.
The function can look the following way
int insert( struct node **linkedlist, int data )
{
struct node *new_element = malloc( sizeof( struct node ) );
int success = new_element != NULL;
if ( success )
{
new_element->data = data;
new_element->next = *linkedlist;
*linkedlist = new_element;
}
return success;
}
If you indeed need a function that appends nodes to the list then it can look the following way
int append( struct node **linkedlist, int data )
{
struct node *new_element = malloc( sizeof( struct node ) );
int success = new_element != NULL;
if ( success )
{
new_element->data = data;
new_element->next = NULL;
while ( *linkedlist != NULL ) linkedlist = &( *linkedlist )->next;
*linkedlist = new_element;
}
return success;
}
Here is a demonstrative program
#include <stdlib.h>
#include <stdio.h>
struct node
{
int data;
struct node* next;
} *start = NULL;
int insert( struct node **linkedlist, int data )
{
struct node *new_element = malloc( sizeof( struct node ) );
int success = new_element != NULL;
if ( success )
{
new_element->data = data;
new_element->next = *linkedlist;
*linkedlist = new_element;
}
return success;
}
int append( struct node **linkedlist, int data )
{
struct node *new_element = malloc( sizeof( struct node ) );
int success = new_element != NULL;
if ( success )
{
new_element->data = data;
new_element->next = NULL;
while ( *linkedlist != NULL ) linkedlist = &( *linkedlist )->next;
*linkedlist = new_element;
}
return success;
}
void printList( struct node* linkedlist )
{
if ( linkedlist == NULL )
{
puts( "Linked list is empty" );
}
else
{
for ( struct node *current = linkedlist; current != NULL; current = current->next )
{
printf( "%d ", current->data );
}
printf( "\n" );
}
}
int main( void )
{
const int N = 10;
for ( int i = 1; i <= N; i++ )
{
if ( i % 2 == 0 ) append( &start, i );
else insert( &start, i );
}
printList( start );
return 0;
}
Its output is
9 7 5 3 1 2 4 6 8 10
I dont think your if statement is correct. I would do it like this
if(linkedlist == null)
{
//head node is null i.e, linked list is empty hence make the new node as head node
linkedlist = new_element;
}else{
//make the new node point to the head
new_element->next = linkedlist;
//make the new node as head node
linkedlist = new_element;
}
In the append function you should
First initialise next of newnode to be null
Check before inserting for empty list, if empty then then make this as head.
If it is not empty then make the new node point to the head of your list and make it the new head.
To reflect back the insertion done in append you should return the modified head from the function(Also modify main accordingly).
Look at this code:-
node *append(node *linkedlist,int data){
node *new_element=NULL;
new_element=(node *)malloc(sizeof(struct node));
new_element->data=data;
new_element->next=NULL;// initialise next of newnode to be null here
// In case of empty list make this as first node
if(linkedlist==NULL)
{
linkedlist=new_element;
}
else
{
new_element->next=(linkedlist);//point the new node to head of list
(linkedlist)=new_element;// make new node as head
}
return linkedlist;
}
int main()
{
start = append(start, 4);
start = append(start, 5);
printList(start);
}
When you pass a parameter in C, it creates a local copy for the function to use. When you modify the parameter inside the function, it only changes the local copy.
void f1( int a )
{
a = 1; // modification does not affect the calling function
}
int b=0;
f1( b );
assert( b == 0 ); // b was not changed by f
In C (or C-style C++), if you want a function to modify a parameter, you need to pass it as a pointer:
void f2( int * a )
{
(*a) = 1;
}
int b=0;
f2( &b );
assert( b == 1 ); // b was changed by f
If that parameter was already a pointer type, then you need to pass it as a pointer to a pointer:
void f3( int * * a )
{
(*a) = (int*) malloc(sizeof(int));
(**a) = 1;
}
int * b = NULL;
f3( &b );
assert( b != NULL ); // the pointer was initialized
assert( *b == 1 ); // the pointed-to value was initialized
The assignment process can be a bit simpler in C++ because you can use reference parameters instead of explicitly specifying the pointer.

In C why does my clearList function not work

I'm trying to clear a list. I keep getting an error that says free() called on an unallocated pointer current. I'm not sure what the problem is I have seen multiple sites use this code.
This is the whole program upon request:
I am only suppose to fill in these three functions.
#include "orderedList.h"
Node *orderedInsert(Node *p, int newval)
/* Allocates a new Node with data value newval
and inserts into the ordered list with
first node pointer p in such a way that the
data values in the modified list are in
nondecreasing order as the list is traversed.
*/
{
Node * q = NULL;
q = (Node*)malloc(sizeof(Node));
q->data = newval;
q->next = NULL;
if (q == NULL)
{
return q;
}
if (p == NULL || newval <= p->data )
{
q->next = p->next;
return q;
}
Node *tmp, *last;
tmp = (Node*)malloc(sizeof(Node));
tmp = p;
while (tmp->next != NULL && tmp->data <= newval)
{
last = tmp;
tmp = tmp->next;
}
q->next = tmp;
last->next = q;
return p;
}
void printList(FILE *outfile, Node *p)
/* Prints the data values in the list with
first node pointer p from first to last,
with a space between successive values.
Prints a newline at the end of the list.
*/
{
Node* temp = p;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
void clearList(Node **p)
/* Deletes all the nodes in the list with
first node pointer *p, resulting in *p
having value NULL. Note that we are passing
a pointer by address so we can modify that
pointer.
*/
{
Node* current = *p;
Node* temp;
while(current != NULL)
{
temp = current->next;
free(current);
current = temp;
}
*p = NULL;
}
sample code
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct node {
int data;
struct node *next;
} Node;
Node *orderedInsert(Node *p, int newval){
Node *q;
if(NULL==(q = (Node*)malloc(sizeof(Node)))){
perror("malloc");
exit(EXIT_FAILURE);
}
q->data = newval;
q->next = NULL;
if (p == NULL || newval <= p->data ){
q->next = p;
return q;
}
Node *tmp = p, *last;
while (tmp != NULL && tmp->data < newval) {
last = tmp;
tmp = tmp->next;
}
q->next = tmp;
last->next = q;
return p;
}
void printList(FILE *outfile, Node *p){
Node* temp = p;
while(temp != NULL){
fprintf(outfile, "%d ", temp->data);
temp = temp->next;
}
fprintf(outfile, "\n");
}
void clearList(Node **p){
Node* current = *p;
while(current != NULL){
Node *temp = current->next;
free(current);
current = temp;
}
*p = NULL;
}
int main (void){
Node *head = NULL;
int i, v;
printf("The generated number\n");
for(i=0; i<10; ++i){
v = rand()%10;
printf("%d ", v);
head = orderedInsert(head, v);
}
printf("\n");
printf("The orderd number\n");
printList(stdout, head);
clearList(&head);
return 0;
}
The code is fundamentally ok, supposing that you don't need to free anything other than the Node objects themselves. The problem is likely with your list. In particular, if it contains a loop then you will see the behavior you describe, because your function will eventually come back to a Node it has already freed.
please read my added comments regarding the OPs code
#include "orderedList.h"
Node *orderedInsert(Node *p, int newval)
/* Allocates a new Node with data value newval
and inserts into the ordered list with
first node pointer p in such a way that the
data values in the modified list are in
nondecreasing order as the list is traversed.
*/
{
Node * q = NULL;
q = (Node*)malloc(sizeof(Node));
q->data = newval; // if q is NULL, then setting an offset from address 0
// which is a real good way to cause a seg fault event
q->next = NULL; // if q is NULL, then setting an offset from address 0
// which is a real good way to cause a seg fault event
if (q == NULL)
{
return q; // odd, returning a null,
// I would expect to return p
}
if (p == NULL || newval <= p->data )
{
q->next = p->next; // odd, if p was null (I.E. adding first entry into list)
// then p->next is some offset from address 0
// a real good way to cause a seg fault event
// if not first entry in list and new data < first entry in list data
// then set q->next to be inserted before the current/existing list
// however,
// (assuming p is the head of the list and not a ptr to the first entry)
// then p->next needs to be set to q
return q;
}
Node *tmp, *last;
tmp = (Node*)malloc(sizeof(Node)); // failed to check if malloc was successful
tmp = p; // overlays the pointer to the 'just' malloc'd memory
// and a good way to cause a seg fault event if malloc failed
-- I stopped checking the code here --
while (tmp->next != NULL && tmp->data <= newval)
{
last = tmp;
tmp = tmp->next;
}
q->next = tmp;
last->next = q;
return p;
}
void printList(FILE *outfile, Node *p)
/* Prints the data values in the list with
first node pointer p from first to last,
with a space between successive values.
Prints a newline at the end of the list.
*/
{
Node* temp = p;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
void clearList(Node **p)
/* Deletes all the nodes in the list with
first node pointer *p, resulting in *p
having value NULL. Note that we are passing
a pointer by address so we can modify that
pointer.
*/
{
Node* current = *p;
Node* temp;
while(current != NULL)
{
temp = current->next;
free(current);
current = temp;
}
*p = NULL;
}

Memory leak detection caused errors

Well basically I had memory leaks. So I wanted to fix them! added free () in some functions. Run valgrind and got successful message All leak memories fixed or smth like that! And after that I have got bunch of errors :( I THINK i have put free() right. It is easy to get confused because there are node as a pointer and node as a struct (look in file.h). Any help appreciated. Thank you. Sorry if this question is simple. i am beginner.....
code in file.h
struct node {
int value;
struct node * next;
};
typedef struct node List;
int is_empty(List *);
List *add_node(List *, int);
List *remove_node(List *, int);
List *create_node(int);
char *tostring(List *);
code in file.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#define STRSIZE 128 /*assume string representation fits*/
/* Return true if the list h is empty
* and false otherwise.
*/
int is_empty(List *h) {
return h == NULL;
}
/* Create a list node with value v and return a pointer to it
*/
List *create_node(int v) {
List *node = malloc(sizeof(List));
free(node);
node->value = v;
node->next = NULL;
return node;
}
/* Insert a new node with the value v at the
* front of the list. Return the new head of
* the list.
*/
List *add_node(List *h, int v) {
List *node = create_node(v);
node->next = h;
return node;
}
/* Remove the first node in the list h that
* has the value v. Return the head of the
* list.
*/
List *remove_node(List *h, int v) {
List *curr = h;
/* Handle the cases when list is empty or the value
* is found at the front of the list */
if (h == NULL) {
return h;
} else if (h->value == v) {
h = h->next;
return h;
}
/* Look for the value in the list (keeping the pointer to
* the previous element so that we can remove the right
* element) */
while (curr->next != NULL && curr->next->value != v) {
curr = curr->next;
}
if (curr->next == NULL) { /* v is not in list so do nothing */
return h;
} else { /* curr->next->value == v */
curr->next = curr->next->next;
return h;
}
}
/* Return a string representation of the list pointed to by h.
*/
char *tostring(List *h) {
char *str= malloc(STRSIZE);
char num[4]; /* assume integer has max. four digits*/
free(str);
str[0] = '\0';
while (h != NULL) {
sprintf(num, "%d", h->value);
strncat(str, num, STRSIZE - strlen(str) - 1);
h = h->next;
}
return str;
}
Possibly you want
temp = curr->next;
curr->next = curr->next->next;
free (temp);
In the current state of your code the remove_node is leaky (leaks the entire list actually, if you remove all the nodes).
Also, to stop memory leak in the tostring function, the caller of this function has the responsibility to free the string returned by tostring.
In your create_node(), why do you have a free()? You should remove that free().
In your remove_node(), you need to always return the head. I think the following recursive method should work. The recursive function will find the node to be deleted and return the next node. It returns the same node if nothing is deleted. Keeps recursing until a node with the value is found or end of the list. The main thing to check is, does it work when
Removing the head node
empty list
Removing the last node
Removing a middle node
The following code is not tested :-).
You mentioned you still have memory leak. As mentioned by Karthik, your toString() function allocates memory and it is up to the caller to free it. So make sure every time you call toString() function, a free() is called also. Otherwise you would be leaking memory.
List *remove_node(List *h, int v) {
List *retval = remove_recurse(h,v);
// if retval is different from head, means head is removed. return new head.
// Otherwise return old head.
return (retval!=h) ? retval : h;
}
List *remove_recurse(List *node, int v) {
if(node==NULL) return NULL;
List *retval = node; // default return current node
if(node!=NULL && node->value==v) { // need to remove node
retval = node->next; // return the next node
free(node); // delete node
}
else {
List *temp = remove_recurse(node->next,v);
// if next node was deleted, point to new node
if(node->next!=temp) node->next=temp;
}
return retval;
}
With this
List *node = malloc(sizeof(List));
free(node);
you allocate a node(List) then let node point to it, then you free what node is pointing to so after free it is pointing to some unallocated space somewhere in memory then you start assigning to that memory:
node->value = v;
node->next = NULL;
which causes undefined behavior, it is definitely wrong but will not be detected by the compiler.
remove free(node)
List *create_node(int v)
{
List *node = malloc(sizeof(List));
node->value = v;
node->next = NULL;
return node;
}
it is more readable if you keep the name of the struct the same as the typedef i.e.
typedef struct node {...} node;
instead of creating a new alias, use instead a better variable name e.g.
node* listStartOfNodes = NULL; // always initialize
1、this function is wrong.
List *create_node(int v) {
List *node = malloc(sizeof(List));
free(node);
node->value = v;
node->next = NULL;
return node;
}
Why do you free(node)? please remove this free(node);.
And the same fault in this function, please remove free(str);
char *tostring(List *h) {
char *str= malloc(STRSIZE);
char num[4]; /* assume integer has max. four digits*/
free(str);
str[0] = '\0';
while (h != NULL) {
sprintf(num, "%d", h->value);
strncat(str, num, STRSIZE - strlen(str) - 1);
h = h->next;
}
return str;
}
2、you should modify this function:
List *remove_node(List *h, int v) {
List *curr = h;
List* freeNode = NULL;
/* Handle the cases when list is empty or the value
* is found at the front of the list */
if (h == NULL) {
return h;
} else if (h->value == v) {
freeNode = h;
h = h->next;
free(freeNode);
return h;
}
/* Look for the value in the list (keeping the pointer to
* the previous element so that we can remove the right
* element) */
while (curr->next != NULL && curr->next->value != v) {
curr = curr->next;
}
if (curr->next == NULL) { /* v is not in list so do nothing */
return h;
} else { /* curr->next->value == v */
freeNode = curr->next;
curr->next = curr->next->next;
free(freeNode);
return h;
}
}
In the function remove_node
List *remove_node(List *h, int v)
{
List *curr = h,*prev;
/* Handle the cases when list is empty or the value
* is found at the front of the list */
if (h == NULL) {
return h;
} else if (h->value == v) {
h = h->next;
free(curr);
return h;
}
while (curr->next != NULL && curr->next->value != v) {
curr = curr->next;
}
if (curr->next == NULL) { /* v is not in list so do nothing */
return h;
} else { /* curr->next->value == v */
prev = curr->next;
curr->next = curr->next->next;
free(prev);
return h;
}
}
You should free the memory when you no longer want that node in your case while removing the node you should free the memory.
And in the function tostring don't free the memory you just allocated free the memory in the called function after the str is no more used or required.

Resources