deleting all the nodes in a linked list - c

Anyone know what's wrong with this recrusive function? It doesn't delete all the nodes
struct contact
{
char FirstName[41];
char LastName[41];
int id;
struct contact *next;
};
void ClearList (struct contact *person)
{
struct contact *temp = person;
if (person == NULL) return;
else
{
person = person->next;
free(temp);
ClearList(person);
}
}
this is my main function
void main()
{
struct contact *person = malloc(sizeof(struct contact));
strcpy (person->FirstName, "John");
strcpy (person->LastName, "Doe");
person->id = 10;
person->next = malloc(sizeof(struct contact));
strcpy (person->next->FirstName, "Will");
strcpy (person->next->LastName, "Smith");
person->next->id = 20;
person->next->next = NULL;
PrintList(person);
ClearList(person);
PrintList(person);
}
when I call PrintList after calling ClearList it still prints out some messy stuffs, how do I fix this?

All the nodes are deleted, but you never clear any pointers. So what you're doing is dereferencing invalid pointers leading to undefined behavior.
The free function doesn't automatically set pointers to NULL.

I really don't like this recursive delete on your linked list. If your linked list has 100 elements you will go 100 functions deep in your stack and probably crash.
I suggest a re-write like this:
void ClearList (struct contact *person)
{
while( person != NULL )
{
struct contact * temp = person
person = person->next;
free(temp);
}
}
Joachim has the correct answer though. Although we have cleared the memory that person points to, "ClearList" does not have the right to set the original pointer to NULL. So either you need to make ClearList take a double pointer so it can set the pointer to NULL, or just set "person" to NULL after calling ClearList.
Double pointer example, call with ClearList(&person);
void ClearList (struct contact ** list)
{
struct contact *person = *list;
while( person != NULL )
{
struct contact * temp = person
person = person->next;
free(temp);
}
*list = NULL;
}

Related

Linked list of chars in C

I'm trying to create a linked list holding char type data.
For some reason, the code does not work. The GCC compiler's warning for function "add_bottom_ListEl" is
"warning: passing argument 2 of 'add_bottom_listEl' makes integer from pointer without a cast"
and
"note: expected 'char' but argument is of type 'char * "
I suspect that there's something wrong about the way I use pointers, but I've tried many, many combinations, passing pointers to the function etc... But nothing seemed to work.
Here's main function and all the others used. MAX_CHAR is defined in all files (#define MAX_CHAR 30)
int main()
{
char name[MAX_CHAR];
scanf("%s", name);
ListEl *head = malloc(sizeof(ListEl));
strcpy(head->name, name);
head->next = NULL;
printf("%s", head->name);
add_bottom_listEl(head, name);
print_listEl(head);
return 0;
}
void add_bottom_listEl (ListEl *head, char name)
{
ListEl *newEl;
while(head->next!=NULL)
{
head=head->next;
}
newEl = (ListEl*) malloc(sizeof(ListEl));
strcpy(newEl->name, name);
newEl->next = NULL;
}
void print_listEl(ListEl* head)
{
puts("print");
ListEl* current = head;
while (current!=NULL)
{
int i=1;
printf("%d.%s\n", i, current->name);
++i;
current = current -> next;
}
}
The ListEl structure is just a regular element of a linked list
struct ListEl
{
char name[MAX_CHAR];
struct ListEl* next;
};
Obviously, I used
typedef struct ListEl ListEl;
Every linked list tutorial on the internet or this site is only showing how to handle lists with integers or numbers in general, but not arrays (chars). Can anyone help me out here?
Your function "add_bottom_listEl" takes one character called "name", not a character array (or a pointer to a character). My guess is you want it to be:
add_bottom_listEl(ListEl *head, char *name)
If your intention in add_bottom_listEl is to modify and pass back head, then head has to be passed as a pointer to a pointer:
void add_bottom_listEl(ListEl** head, char* name) {
if ( head == NULL ) {
//head is invalid, do nothing
return;
}
//Use calloc instead of malloc to initialise the memory area
ListEl* newEl = (ListEl*)calloc(1, sizeof(ListEl));
//Ensure only name of the permissible length is copied
strncpy(newEl->name, name, MAX_CHAR-1);
//No need to do this now...calloc will initialise it to NULL
//newEl->next = NULL;
if ( *head == NULL ) {
//No nodes in list yet, this is the first
*head = newEl;
} else if ( *head != NULL ) {
//Find the end of the list
while((*head)->next!=NULL) {
*head = (*head)->next;
}
}
//Add the new node to the list
*head = newel;
}
When you call this modified version of the function pass the address of the pointer:
add_bottom_listEl(&head, name);
You can make your typedef more readable by doing this:
typedef struct _listEl {
char name[MAX_CHAR];
struct _listEl* next;
} ListEl;
The line
void add_bottom_listEl (ListEl *head, char name)
should be
void add_bottom_listEl (ListEl *head, char* name)

Changing an int value in struct which is in linked list

I have a struct:
struct person{
char* name;
char* surname;
int age;
};
And I use this struct inside another struct for linked list:
struct listNode {
struct person data;
struct listNode *nextPtr;
};
typedef struct listNode ListNode;
typedef ListNode *ListNodePtr;
I am trying to insert one struct into the linked list. After that I want to get this struct by index and change the age value. In main, I have these codes:
int main() {
ListNodePtr startPtr = NULL;
struct person p;
p.age = 22;
p.surname = "Pilkington";
p.name = "Anthony";
insert(&startPtr, p);
printf("Age1:%d\n", p.age);
struct person p2 = getStruct(&startPtr, 0);
p2.age++;
printf("Age2:%d\n", p2.age);
struct person p3 = getStruct(&startPtr, 0);
printf("Age3:%d\n", p3.age);
return 0;
}
Now, my insert function and getStruct function are working correctly. But I cannot increment the age value. My output is:
Age1:22
Age2:23
Age3:22
I want to make the Age3 23. In insert function, I already allocate memory with malloc, but why can't I change the first value? How can I change this value without removing/adding the node from list?
I search the subject but I cannot find the answer that I want. So, If you can help me I appreciate that.
---EDIT-----
Sorry for not posting getStruct method, I am adding it below:
struct person getStruct(ListNodePtr *sPtr, int index) {
ListNodePtr currentPtr;
struct person c;
int counter = 0;
currentPtr = *sPtr;
while (currentPtr != NULL) {
if (counter == index) {
c = currentPtr->data;
}
counter++;
currentPtr = currentPtr->nextPtr;
}
return c;
}
Here:
struct person p2 = getStruct(&startPtr, 0);
You return a person by value, which is to say you make a copy. Then you modify the copy, not the original. You'll need to do this instead:
struct person *p2 = getStruct(&startPtr, 0);
p2->age++;
printf("Age2:%d\n", p2->age);
That is, return a pointer, which you can use to modify what it points to. This also requires a slight change to getStruct(), whose code you haven't posted.

linked list c losing head address

I'm implementing a "car fleet" management tool. For start, I declared a "Car" struct:
typedef struct Car{
char ID[9];
struct Car* next;
} Car;
My fleet will simply be a linked list of cars. I begin with an empty head, and add links to it via add_car function.
void add_car (struct Car* cars_fleet){
struct Car* car = malloc (sizeof (car));
scanf("%s",car->ID);
car->next = NULL;
if (cars_fleet == NULL){
printf ("creating new list\n");
cars_fleet = car;
}
else {
printf ("appending\n");
Car* tmp = cars_fleet;
while (! (tmp->next == NULL))
tmp = tmp->next;
tmp->next = car;
}
}
And this is my main program:
int main(){
Car* cars_fleet = NULL;
add_car(cars_fleet);
}
Now, for some reason, my program treats all links as if they were the first one. I assume this is happening because of wrong memory allocation, perhaps at "cars_fleet = car". Any hints, tips or solutions? thanks.
You are passing the pointer by value. So any change in function void add_car (struct Car* cars_fleet) has no effect outside the function. You need to change function prototype :
void add_car (struct Car* cars_fleet)
Like this:
void add_car (struct Car** cars_fleet)
Then use, inside the function :
*cars_fleet = car
While, in main, call:
add_car(&cars_fleet);
You are assigning to a local variable in add_car, change to:
Car *add_car(void){/* Car *x = malloc(...); ... return x */};
And in main:
Car* cars_fleet = NULL;
cars_fleet = add_car();
How am I supposed to iterate through cars_fleet's links if I do not
pass it to add_car? and whose address am i returning? the address of
the new cars_fleet with the car added?
In your question you are passing a NULL, so there is no chance to iterate throug links, divide, use a function to get a fresh node and another one to insert this new Car in the list (passing the tail).

How to insert new item into linked list in C (at the end of the list)

I'm trying to get my head around Linked Lists in C, and have ended up confusing myself.
My question is: Is this correctly inserting the different people at the end of the list? Or is it simply inserting them at the beginning?
At the moment I'm trying to insert a new person at the end of my linked list. My struct is defined as follows:
struct person {
char *name;
int age;
struct person *next;
};
I'm accessing the data from pre allocated arrays:
#define HOW_MANY 7
char *names[HOW_MANY]= {"Simon", "Suzie", "Alfred", "Chip", "John","Tim","Harriet"};
int ages[HOW_MANY]= {22, 24, 106, 6, 18, 32, 24};
Here is my insert at the end function:
static struct person * insert_end(struct person *people, char *name, int age)
{
struct person *newPeople = (struct person*)malloc(sizeof(struct person));
if (newPeople == NULL)
{
perror("Memory allocation failed");
abort();
}
newPeople->name = name;
newPeople->age = age;
if (people == NULL)
{
newPeople->next = people;
people = newPeople;
return people;
}
else {
while(newPeople->next != NULL)
{
newPeople->next = people;
}
people = newPeople;
return people;
}
}
I think that the while loop within the function is not being executed, and I can't figure out why.
Thanks!
You are indeed putting the new record at the beginning of the list. You must realize that newPeople->next will always be null as it is freshly allocated. You need to iterate through people until people->next is NULL and then asign newPeople.
newPeople->next is not given a value before it is read leading to undefined behavior. So code was not properly inserting at the end nor beginning.
while(newPeople->next != NULL) // undefined behavior
The following does not initialize the objected pointed to by newPeople.
struct person *newPeople = (struct person*)malloc(sizeof(struct person));
Code appears to be making a circular linked-list with the following.
if (people == NULL) {
newPeople->next = people;
...
Although that is OK, it is more common to form a NULL terminated list and I'll assume that is your goal.
static struct person * insert_end(struct person *people, char *name, int age) {
// struct person *newPeople = (struct person*)malloc(sizeof(struct person));
// No need for cast. Recommend sizeof object rather than sizeof type
struct person *newPeople = malloc(sizeof *newPeople);
if (newPeople == NULL) {
...
}
// Best to initial all fields.
newPeople->name = name;
newPeople->age = age;
newPeople->next = NULL;
if (people == NULL) {
return newPeople; // New head node
}
// else not needed
// else {
// Code was attempting to march down wrong list.
// Create `walker` instead.
struct person *walker = people;
while(walker->next != NULL) {
walker = walker->next;
}
walker->next = newPeople;
return people;
}

how to use a pointer to pointer to insert in a linked list

Think is a function to insert new element in the order of name.
I knew how to do it if I use a if to separate condition of inserting at the start and others. But I was asked to merge the if and while into a single while loop.
How could i integrate the insert function into one while loop with pointer to pointer?
person* insert_sorted(person *people, char *name, int age)
{
person *p=NULL;//,*t=NULL,*q=NULL;
person *ptr= people;
person **ptr2ptr=&ptr;
p=malloc(sizeof(person));
if ( p == NULL ){
printf("malloc() failed\n");
return NULL;
}
else {
p->name = name;
p->age = age;
if ( people == NULL ){ // empty list
people = p;
people->next =NULL;
}
else{
*ptr2ptr = ptr;
while( (*ptr2ptr) !=NULL )
{
if ( compare_people(p, people)<=0 ) // insert at the start
break;
else if ( (*ptr2ptr)->next == NULL) //insert at the end
break;
else if ( compare_people(*ptr2ptr, p) <=0 && compare_people( p, (*ptr2ptr)->next)<=0 )//insert at the middle
break;
*ptr2ptr = (*ptr2ptr)->next;
}
//insert at the end
p->next = (*ptr2ptr)->next;
(*ptr2ptr)->next = p;
}
}
eInstead of trying to find the person element in the list which has no successor, try to find the first null pointer. Something like this (untested):
void insert_sorted(person **p, char *name, int age)
{
while (*p) {
p = &(*p)->next;
}
*p = malloc( ... );
/* ... */
}
This kind of problem is usually best solved with a pen an paper and then drawing a couple of boxes and arrows. The idea is that your 'p' pointer no longer points at a specific person but rather at some pointer which points to a person.
There can be a few options.
I would move the if inside the compare_people function provided that you can change it. After all, adding the very first element in a list is like adding a new "top of the list" element (of least of the list). I know this can be seen as "cheating". And it is, indeed!
You can create a "fake" list element which will always be tested to be the first (or the last) of the sorted list (like with an empty name).
So the list won't ever be empty and there won't ever be a "check for an empty list" test. Of course the content of that fake item needs to comply with the semantics of the compare_people function.
At a cost that's slightly higher than the current O(n), O(n*log(n)) actually, you could use a temporary support structure (like an array of pointers) and qsort() from stdlib.h in order to keep the list sorted.
Finally, implement insertion sort which would exploit the fact that the original set is already sorted before inserting the new element.
The function can be written the following way (without testing because I do not know some definitions of the list)
person * insert_sorted( person **people, char *name, int age )
{
person *p = malloc( sizeof( person ) );
if ( p == NULL )
{
printf( "malloc() failed\n" );
}
else
{
p->name = name;
p->age = age;
person *prev = NULL;
person *current = *people;
while ( current && !( compare_people( p, current ) < 0 ) )
{
prev = current;
current = current->next;
}
p->next = current;
if ( prev == NULL ) *people = p;
else prev->next = p;
}
return p;
}
And the function should be called like
insert_sorted( &people, name, age );
^^^^^^^
Without testing:
person* insert_sorted(person** people, char *name, int age) {
person* added = malloc(sizeof(person));
added->name = name;
added->age = age;
added->next = NULL;
person* previous = NULL;
person* current = *people;
while (current && compare_people(current, added) <= 0) {
previous = current;
current = current->next;
}
if (!people) {
*people = added;
} else {
previous->next = added;
added->next = current;
}
return added;
}
The way you use the pointer to pointer doesn't make use of the indirection. You only write (*ptr2ptr) where you would normally have written ´ptr`.
The idea of using a pointer to a node pointer is that by adding one level of indirection, you are able to access and modify the head pointer from the calling function. If you just pass in a node pointer, all changes to that pointer are local to the insert function and will not update the head pointer of your list in the calling function if necessary.
Your function signature should already pass a pointer to a node pointer:
void insert(person **p, const char *name, int age);
and call it like so:
person *head = NULL;
insert(&head, "Betty", 26);
insert(&head, "Ralph", 23);
insert(&head, "Chuck", 19);
insert(&head, "Alice", 42);
insert(&head, "Simon", 34);
When you enter the fuction, p is the address of head in the calling function. As you iterate through the list with
p = &(*p)->next;
*p hold the address of the next pointer of the previous node. p is a "whence" pointer: It holds the address of the pointer that points to the ode you are processing. That means an empty list isn't a special case any longer.
Your function requires to return the new head pointer. It is easy to forget to assign it and it also adds some redundancy to the call. The pointer-to-pointer approach also fixes this.
Here's how your insertion code could look like with a function that takes a pointer to pointer as argument:
struct person {
const char *name;
int age;
person *next;
};
int compare(const person *p1, const person *p2)
{
return strcmp(p1->name, p2->name);
}
person *person_new(const char *name, int age)
{
person *p = malloc(sizeof(*p));
p->name = name;
p->age = age;
p->next = NULL;
return p;
}
void insert(person **p, const char *name, int age)
{
person *pnew = person_new(name, age);
while (*p && compare(*p, pnew) < 0) {
p = &(*p)->next;
}
pnew->next = *p;
*p = pnew;
}
here i found the most useful answer to this question:http://www.mvps.org/user32/linkedlist.html
ptr2ptr = &people;
while ( *ptr2ptr!=NULL && compare_people(*ptr2ptr,p) ) {
ptr2ptr = &(*ptr2ptr)->next;
}
p->next = *ptr2ptr;
*ptr2ptr = p;

Resources