Swapping elements in a linked list - c

So I'm running into a little problem with my sorting function.
While it's doing what it's supposed to in the second edge case,
the elements get swapped and then swapped back continuously.
The example here happens when Sven(25) and Bob(22) meet.
void sortPerson(person *pers, person* newPerson)
{
if(pers == NULL || pers->next == NULL)
{
printf("List is emtpy");
return;
}
person* tempValue;
person* prev = pers;
person* curr = pers->next;
//person* newValue = pers;
while(prev != NULL && curr != NULL)
{
//first edge case
//adds a new person
if(prev->age < newPerson->age)
{
newPerson->next = prev->next;
prev->next = newPerson;
}
//second edge case
//swapping process when prev age greater than curr age
//forming a decending order of ages
if(prev->age > curr->age)
{
tempValue = prev;
prev = prev->next;
prev->next = tempValue;
printf("\nPerson age: %d\n", tempValue->age);
printf("loop test\n");
printf("%d and %d\n",prev->age, prev->next->age);
}
//third edge case
//if age is the same do nothing
if(prev->age == curr->age)
{
return;
}
prev = prev->next;
curr = curr->next;
}
}
This function returns a new person
person* addPerson( person *newPers ){
return newPers;
}
And here is my main if you want to test it yourself
int main(){
person* person1 = construct_person("Max", 20);
person* person2 = construct_person("Sven", 25);
person* person3 = construct_person("Bob", 22);
person* person4 = construct_person("John", 23);
person* newPerson = construct_person("Markus", 21);
person1->next = person2;
person2->next = person3;
person3->next = person4;
//person4->next = addPerson(person1, newPerson);
//swapPerson(person1);
sortPerson(person1, addPerson(newPerson));
printperson(person1);
free(person1);
free(person2);
free(person3);
free(person4);
free(newPerson);
}
My struct person and constructor
typedef struct person person;
struct person{
char *name;
int age;
person *next;
};
person* construct_person(char *name, int age)
{
person* pers = (person*) malloc (sizeof(person));
pers->name = name;
pers->age = age;
pers->next = NULL;
return pers;
}
I suspect the problem is that my 'prev' struct pointer gets altered throughout the process, but I'd like a second opinion and potential fix.
*Note I'm only freeing it like this because this is testing only and I'm not gonna add more people else I'd done so iteratively.

Some remarks:
The name sortPerson is not well chosen. It should better be named insertPerson
You cannot hope to sort a list with just one iteration and a swap here and there during that iteration. If that were possible, you would have invented the most efficient sorting algorithm. So, this cannot work -- at least not always.
Instead of implementing some sort algorithm (like bubble sort, selection sort, ...), make sure you add all persons using that insertPerson function. That way you can be sure that the list into which a new person is inserted, is already sorted. This way you don't have to swap elements at all. You only have to find the correct insertion point and insert the new person there. So in the main program you should not be fiddling with next pointers. Leave that to this insertPerson function.
When a list is empty or just has one person, you should still insert the new person. It makes no sense to not do it when the list is empty (or just has one element).
The only base case is when you need to insert the person before all other persons. In that case the reference to the first person needs to change, as the new person becomes that first person. One way to make that work, is to make the first parameter a call-by-reference. In other words: pass the address of where the first person pointer is stored, so that this pointer can be changed, and the caller will have access to that change.
I don't see the logic in omitting an insertion when the age of the new person matches the age of a person already in the list. Surely your list should allow Bob and Alice, even when they are both 28.
There are several other points to make...
Here is a working version. I had to make some assumptions about the code that you didn't share, but the principle should be clear even if your code is different:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct person_s {
int age;
char name[100];
struct person_s * next;
} person;
person* construct_person(char* name, int age) {
person* p = malloc(sizeof(person));
strcpy(p->name, name);
p->age = age;
p->next = NULL;
return p;
};
void insertPerson(person** youngest, char* name, int age)
{
person* newPerson = construct_person(name, age);
// Even when list is empty, still add it
if (*youngest == NULL || (*youngest)->age > age) {
newPerson->next = *youngest;
*youngest = newPerson;
return;
}
person* prev = *youngest;
person* curr = prev->next;
// Search insertion point
while (curr != NULL && curr->age < age) {
prev = curr;
curr = curr->next;
}
// Found it:
prev->next = newPerson;
newPerson->next = curr;
}
void printList(person* pers) {
while (pers != NULL) {
printf("%s is %d years old.\n", pers->name, pers->age);
pers = pers->next;
}
}
void freeList(person** pers) {
while (*pers != NULL) {
person* temp = *pers;
*pers = (*pers)->next;
free(temp);
}
}
int main(){
person* personList = NULL;
insertPerson(&personList, "Max", 20);
insertPerson(&personList, "Sven", 25);
insertPerson(&personList, "Bob", 22);
insertPerson(&personList, "John", 23);
insertPerson(&personList, "Markus", 21);
printList(personList);
freeList(&personList);
}

Related

How can I alphabetize this linked list?

I am writing a program that has names and ages entered into it. The names can then be called and the age of the person will be printed out. If the person does not exist in the list it prints their age as -1. If a name is entered with a new age that is already in the list, the new entry is not added. Currently it appears the names are sorted by the order that I input them. How can I sort them alphabetically by only changing the code for the function add? This code is compileable and works as intended except for the non-alphabetized list.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct person {
char *name;
int age;
struct person *next;
} Person;
void print(Person *); // prints the entire list
Person *add(Person *, char *, int); // adds a new node to the list
int getAge(Person *, char *); // returns the age of the person or -1 if not found
int main(void) {
char input1[100];
int input2;
Person *myList = NULL;
printf("Enter a person's name (one word) and age : ");
scanf("%s %d", input1, &input2);
while (input2 != 0) {
myList = add (myList, input1, input2);
printf("\n\nThe list is now : "); print(myList);
printf("Enter a name (one word) and age, enter 'xxx' and 0 to exit : ");
scanf("%s %d", input1, &input2);
}
printf("\n\nThe final list is "); print(myList);
printf("\n\nEnter the name of a person to look up their age : ");
scanf("%s", input1);
while ( strcmp(input1, "xxx") != 0 ) {
printf("\t%s is %d years old\n", input1, getAge(myList, input1) );
printf("Enter a name to look up their age or 'xxx' to exit : ");
scanf("%s", input1);
}
return 0;
}
void print(Person *ptr) {
while (ptr) { printf("[%s-%d] ", ptr->name, ptr->age); ptr = ptr->next; }
printf("\n");
return;
}
//adds person to list if the person does not exist already
Person *add(Person *ptr, char *n, int a) {
Person *newNode = malloc( sizeof(Person) );
int duplicate = 1;
Person *dummy = ptr;
while (dummy) {
if(strcmp(dummy->name, n) == 0) {
printf("Name Already Exists in List! Please retry with other name..\n");
duplicate=-1;
break;
}
else
dummy = dummy->next;
}
if (duplicate!=-1) {
newNode->name = malloc( strlen(n) + 1 );
strcpy(newNode->name, n);
newNode->age = a;
newNode->next = ptr;
return newNode;
}
duplicate = 1;
return ptr;
}
//function to find age of the passed person
int getAge(Person *ptr, char *name) {
while (ptr) {//while loop to traverse entire linked list elements (All persons one by one)
if(strcmp(ptr->name, name) == 0) //comparing person name in the list with the search key name
return ptr->age; //if found, returning the age of that person
else
ptr = ptr->next; //if not found, check in next node of linked list
}
return -1; // if not found, even after visting all nodes, return -1
}
You can do an insertion sort. Each time you add a new record, you scan through the list to see where it belongs and insert it there. This could be combined with your scan for duplicates.
Person *add(Person *head, char *n, int a) {
char empty[1] = "";
Person sentinel = {0};
sentinel.name = empty;
sentinel.next = head;
Person *p = &sentinel;
while (p) {
int cmp = p->next ? strcmp(n, p->next->name) : -1;
if (cmp == 0) {
printf("Name Already Exists in List! Please retry with another name..\n");
break;
}
if (cmp < 0) {
Person *newNode = malloc( sizeof(Person) );
newNode->name = malloc( strlen(n) + 1 );
strcpy(newNode->name, n);
newNode->age = a;
newNode->next = p->next;
p->next = newNode;
break;
}
p = p->next;
}
return sentinel.next; // a possibly-updated copy of head
}
Insertion sort always compares the new element to the next element (rather than to the current element). This makes dealing with the first element awkward, especially in a list. We get around that with a temporary "sentinel" that we pretend is just before the head of the list.
There are other approaches. You can create the new node at the head of the list and then slide it down until it's in position. If you encounter a duplicate, you remove the new one and patch up the list. Insertion sorts in other data structures typically work from the tail back toward the head, but that won't work with a singly-linked list.
I wrote something similar where I sorted student ID's. You should try doing a swap. Declare a temp variable and used that to swap. The code is something like.
int temp,
first_name,
last_name;
temp = first_name;
first_name = last_name;
last_name = temp;
Hope that gives you an idea!
Edit: What the other person suggested is a good idea as well, an insertion sort.
Your question has already been answered, but I'd like to point out another approach to adding a node. The list is defined by its head node. When iserting elements, the head node may change. (It will change in your current code, which adds elements at the front.) In order to reflect the change, you return the new head:
myList = add(myList, input1, input2);
This is redundant, because you have to specify myList twice. It is also legal to discard the result returned from a function, so there is the possibility of an error. If you pass in a pointer to the head pointer, you will eliminate the redundancy.
add(&myList, input1, input2);
The ´&will indicate thatmyList` may change. And you can now use the return value for something else, for example a pointer to the newly inserted node or null if the name was already there.
Inserting a person at the front (unconditionally) looks like this:
Person *add_front(Person **ptr, const char *name, int age)
{
Person *p = person_new(name, age);
p->next = *ptr;
*ptr = p;
return p;
}
Inserting at the end requires to walk the list first:
Person *add_back(Person **ptr, const char *name, int age)
{
Person *p = person_new(name, age);
while (*ptr) {
ptr = &(*ptr)->next;
}
p->next = *ptr;
*ptr = p;
return p;
}
Note how you do not need to treat the empty list as a special case. Adrian's solution eliminates the special case with a sentinel element. Here, it is eliminated by the pointer itself: ptr points to the list head pointer in the calling function at first. As you walk through the list, it points to the next pointer of the previous node. Updating *ptr updated the pointer that took us to the current node.
The function person_new creates a new node. I find it tidier to make this a separate function, which you could call from other functions:
Person *person_new( const char *name, int age)
{
Person *p = malloc(sizeof(*p));
p->name = malloc(strlen(name) + 1);
strcpy(p->name, name);
p->age = age;
p->next = NULL;
return p;
}
Now the function you want, which inserts the node in alphabetical order, looks like this:
Person *add(Person **ptr, const char *name, int age)
{
while (*ptr && strcmp((*ptr)->name, name) < 0) {
ptr = &(*ptr)->next;
}
if (*ptr == NULL || strcmp((*ptr)->name, name) != 0) {
Person *p = person_new(name, age);
p->next = *ptr;
*ptr = p;
return p;
}
return NULL;
}
When you look up a node by name, you can stop the search short when the current node is alphabetically larger than the name you're looking for:
const Person *find(const Person *ptr, const char *name)
{
while (ptr) {
int cmp = strcmp(ptr->name, name);
if (cmp > 0) break;
if (cmp == 0) return ptr;
ptr = ptr->next;
}
return NULL;
}
You can see the code in action here.

Insert at start of list

I am writing a simple function in c++.
I get some errors with my code. THERE is no output.
Also I need to call free after remembered where the next index.
I dont know how to do that.
Do I need to free current inside the while loop after printf?
here my code
#include <stdio.h>
#include<stdlib.h>
/* these arrays are just used to give the parameters to 'insert',
to create the 'people' array
*/
#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};
/* declare your struct for a person here */
typedef struct{
char* name;
int age;
struct person *next;
}
person;
static void insert( person *headptr, char *name, int age)
{
person *ptr=malloc(sizeof(person));
if(ptr==NULL) abort();
//assign to structure field
ptr->name = name;
ptr->age = age;
//link new object into the list
ptr->next=headptr;
headptr=ptr;
}
int main(int argc, char **argv)
{
/* declare the people array here */
person *headptr=NULL;
// Make a copy of the pointer to the head item in the list
for (int index=0;index < HOW_MANY;index=index+1)
{
insert(headptr, *(names+index), ages[index]);
}
person *current=NULL;
// current will be set to NULL when it reaches the end
while(current != NULL)
{
// print out the item information
printf("name: %s, age: %i\n",current -> name, current-> age);
// Now move to the next item in the linked list
current= current -> next;
}
}
There are several problems in your code.
Your struct's next field is declared using an unknown type.
Your insert() function is not updating the headptr variable in main().
You get no output because the current variable in main() is initialized to NULL instead of headptr, so there is nothing for the loop to do.
You are leaking the allocated memory.
Try something more like this instead:
#include <stdio.h>
#include <stdlib.h>
/* these arrays are just used to give the parameters to 'insert', to create the 'people' array */
#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};
/* declare your struct for a person here */
typedef struct person {
char* name;
int age;
struct person *next;
} person;
static void insert(person **headptr, char *name, int age) {
person *ptr = malloc(sizeof(person));
if (!ptr) abort();
//assign to structure fields
ptr->name = name;
ptr->age = age;
//link new object into the list
ptr->next = *headptr;
*headptr = ptr;
}
int main(int argc, char **argv) {
/* declare the people array here */
person *headptr = NULL;
// insert items at the head of the list
for (int index = 0; index < HOW_MANY; ++index) {
insert(&headptr, names[index], ages[index]);
}
person *current = headptr;
// current will be set to NULL when it reaches the end
while (current) {
// print out the item information
printf("name: %s, age: %i\n", current->name, current->age);
// Now move to the next item in the linked list
current = current->next;
}
// free the items
current = headptr;
while (current) {
person *next = current->next;
free(current);
current = next;
}
return 0;
}
To insert at the front of a linked list, I would suggest that you pass a pointer to the head of the list, not the head, resulting in something like this example (written from memory, but should work):
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct list {
struct list *next;
int value;
} list;
void list_push(list **phead, int value) {
list *front = malloc(sizeof(struct list));
front->next = *phead;
front->value = value;
*phead = front;
}
bool list_insert(list **phead, int value, int index) {
list **ptr = phead;
while (index-- > 0) {
if (!*ptr) return false;
ptr = &(*ptr)->next;
}
list_push(ptr, value);
return true;
}
void list_dump(list *head) {
while (head) {
printf("%d\n", head->value);
head = head->next;
}
}
void main(void) {
list *head = NULL; // empty list
list_push(&head, 23);
list_insert(&head, 42, 1);
list_insert(&head, 13, 0);
list_dump(head);
}
You need to modify your insert method like below:-
static person* insert(person *headptr, char *name, int age)
{
if (headptr == NULL) {
headptr = (person*)malloc(sizeof(person));
headptr->name = name;
headptr->age = age;
headptr->next = NULL;
}
else{
person *ptr = (person*)malloc(sizeof(person));
if (ptr == NULL) abort();
//assign to structure field
ptr->name = name;
ptr->age = age;
//link new object into the list
ptr->next = headptr;
headptr = ptr;
}
return headptr;
}
And now you need to call the above method like below:-
// Make a copy of the pointer to the head item in the list
for (int index=0;index < HOW_MANY;index=index+1)
{
headptr = insert(headptr, *(names+index), ages[index]);
}
Now you need to print your list like below:-
person *current=headptr;//you need to point to the first node of the list
// current will be set to NULL when it reaches the end
while(current != NULL)
{
// print out the item information
printf("name: %s, age: %i\n",current -> name, current-> age);
// Now move to the next item in the linked list
current= current -> next;
}

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;

deleting all the nodes in a linked list

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

Resources