Why am I getting segmentation fault on my code? - c

I am trying to add an item to the linked list by traversing the list to create the next node. and the last node in the list to point to the newly created node. But I am getting a core dump segmentation fault on it.
void linked_list_add(list_node_t *head, void *item)
{
list_node_t *temp = head;
while(temp->next != NULL)
{
temp = temp->next;
}
list_node_t *new_node = (list_node_t *)malloc(sizeof(list_node_t));
new_node->data = item;
new_node->next = NULL;
new_node->prev = temp;
//if(temp != NULL)
// temp->next = new_node;
// new_node->prev = temp;
}
TEST_F(LinkedList, Add)
{
int i = 3;
linked_list_add(list, &i);
ASSERT_EQ(list->next->data, &i);
i = 4;
linked_list_add(list, &i);
ASSERT_EQ(list->prev->data, &i);
i = 5;
linked_list_add(list, &i);
ASSERT_EQ(list->next->data, &i);
}

This is an answer to summarize the comments.
There are likely at least 3 issues with the code as written:
When the code void linked_list_add(list_node_t *head, void *item) is passed arguments, you generally want to be able to handle a NULL pointer for head. It looks like the while loop immediately goes into searching for the end of the list even if the head is null.
The newly added node, new_node gets the prev pointer updated so that the backwards searchs will be and shouldn't segfault. However, the forward searching isn't preserved. By this I mean that the last non-NULL node in the linked list doesn't have the next pointer pointing to the new_node.
The test ASSERT_EQ(list->prev->data, &i); is likely accessing either a random memory location or a NULL pointer. Given that the OP didn't post the declaration of the list struct it is difficult to say what the default values are/will be. However, unless this list is circular, the value of list->prev is an uninitialized pointer. Depending on your setup (e.g. if there is setup code for the linked list that sets the pointers to null, you could be accessing a NULL pointer there too.
I hope this helps the OP solve their coding problem(s).

Related

Merge Sort On Linked List in C

Merge sort is often preferred for sorting a linked list. The slow random-access performance of a linked list makes some other algorithms (such as quicksort) perform poorly, and others (such as heapsort) completely impossible.
I have been struggling to do Merge Sort on a linked list. It keeps throwing back an error. I'm providing the code I've tried to execute. Please do help me out.
It keeps giving runtime error.
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *next;
};
struct node *SortedMerge(struct node *a, struct node *b);
void FrontBackSplit(struct node *source, struct node *frontref, struct node *backref);
struct node *Create(struct node *head, int num) {
struct node *newnode, *temp;
newnode = (struct node *)malloc(sizeof(struct node));
newnode->data = num;
newnode->next = NULL;
if (head == NULL) {
head = newnode;
temp = newnode;
} else {
temp->next = newnode;
temp = temp->next;
}
temp->next = NULL;
return head;
}
struct node *display(struct node *head) {
struct node *temp;
temp = head;
while (temp != NULL) {
printf("%d->", temp->data);
temp = temp->next;
}
printf("NULL");
return head;
}
struct node *MergeSort(struct node *head) {
struct node *headref, *a, *b;
headref = head;
if ((head == NULL) || (head->next) == NULL) {
return;
}
FrontBackSplit(headref, a, b);
MergeSort(a);
MergeSort(b);
head = SortedMerge(a, b);
return head;
}
void FrontBackSplit(struct node *source, struct node *frontref, struct node *backref) {
struct node *fast, *slow;
slow = source;
fast = source->next;
while (fast != NULL) {
fast = fast->next;
if (fast != NULL) {
slow = slow->next;
fast = fast->next;
}
}
frontref = source;
backref = slow->next;
slow->next = NULL;
}
struct node *SortedMerge(struct node *a, struct node *b) {
struct node *result;
result = NULL;
if (a == NULL) {
return (b);
}
else if (b == NULL) {
return (a);
}
if (a->data <= b->data) {
result = a;
result->next = SortedMerge(a->next, b);
} else {
result = b;
result->next = SortedMerge(a, b->next);
}
return result;
}
int main() {
struct node *head = NULL;
int i, n, num;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &num);
head = Create(head, num);
}
head = MergeSort(head);
display(head);
}
There are a couple of problems with the code, and which one triggers the error you are seeing I cannot say, but I will point out a few of them below. Take Create():
struct node *Create(struct node *head, int num)
{
struct node *newnode, *temp;
newnode=(struct node *)malloc(sizeof(struct node));
newnode->data=num;
newnode->next=NULL;
if(head==NULL) {
head=newnode;
temp=newnode;
} else {
temp->next=newnode;
temp=temp->next;
}
temp->next=NULL;
return head;
}
I cannot work out exactly what it is supposed to do, to be honest. Maybe add a new node to a list, represented by a head link? It doesn't do that. You create a new node
newnode=(struct node *)malloc(sizeof(struct node));
which I would suggest you write as
newnode = malloc(sizeof *newnode);
You don't need to cast void *, so you don't need to cast the result of malloc(), and using sizeof *newnode rather than sizeof(struct node) is safer. But the code works correctly in the form you have, so there is not a problem there. However, what happens with that node depends on head. If head is NULL, you point it at the new node, and through temp you (re)assign the new node's next to NULL. So now you will return an updated head that consists of the new node as a single element list. That matches my guess at what the function should do.
However, if head is not NULL, you put the new node in temp->next, which is a problem, since temp isn't initialised. You write to temp in the if(head==NULL) branch, but you dereference it in the else branch, where it can point anywhere. I am surprised if you don't get a segmentation fault from time to time here. It isn't necessary to assign the new node to temp->next, though, because immediately afterwards you change temp to point to temp->next, which is where you just put newnode, so temp = newnode would do the trick, without the segfault. But not all is well if we do that. We now would have the new node in temp (with the next pointer, again, reassigned to NULL) and then we return head. We didn't connect head with newnode anywhere, if we took the else branch. So calling Create() with a non-NULL head creates a new node, throws it away (and leaking memory), and that is all that does.
So while my guess is that Create() should add a new to a list, represented by head, or something to that effect, what it actually does is create a single-element list if the first argument is NULL, and leak sizeof(struct node) memory while doing nothing if head != NULL.
That being said, the code might work by pure luck of course. When I tried it with clang with zero optimisation, I somehow managed to build a list correctly. This is luck, though. It won't work in general. I suspect that what happens is that the repeated calls to Create() in the loop in main() happens to leave the last node you created (and wrote to temp) at the same stack location as the uninitialised temp in the next call. So by pure luck, putting the new node in temp's next appends the new node to the last node you created. It was really interesting working that one out :) But don't rely on this, of course. It is a combination of several lucky circumstances. Add optimisation flags, and the compiler will change the stack layout, and the code will break. Call other functions between successive calls to Create() and the stack will change, and then you don't have the last link on the stack any longer. And the code will break. It is a very unstable situation if this works at all.
If you just want to add a new node to a list, make a prepend function. Something like
struct node *prepend(int val, struct node *list)
{
struct node *n = malloc(sizeof *n);
if (n) {
n->data = val;
n->next = list;
}
return n;
}
(I haven't tested it, so there might by syntax errors, but it will be something like that...you need to figure out what to do if malloc() fails, but you could just abort() if you don't want to deal with it).
There is nothing wrong with display(), except that I don't understand why it is in lower-case when the other functions are in camel-case. You don't need temp, you can use head in the while-loop, but that is a style choice. The function works as intended.
With MergeSort(), however, we have another problem. I am surprised that your compiler didn't scream warnings at you here. It should really give you an error, with the right flags, but at the very least an error. When you test if the list is empty or a singleton, you return, but not with a node. The function should return a struct node *, so just using return will not give you anything useful.
if((head==NULL) || (head->next)==NULL){
return;
}
If the base case of the recursion returns garbage, obviously the whole recursion tumbles. Otherwise, assuming that the FrontBackSplit() and SortedMerge() work, the function looks okay. You don't need the extra headref variable, it is just a synonym for head, but there is nothing wrong with having it. The compiler will get rid of it for you. There isn't any need to assign the merged lists to head and then return head either. You can just return SortedMerge(a,b). But again, your compiler will handle that for you, once you turn on optimisation. Except for the base case, I believe the function should work.
In FrontBackSplit(), I get the impression that you want to get the frontref and backref values back to the caller. Otherwise, the function doesn't do anything. But when you are modifying the function parameters, you are not changing the variables in the caller's scope. You need to pass the two pointers by reference, which means that you need to use pointers to pointers. Change it to something like this:
void FrontBackSplit(struct node *source,
struct node **frontref,
struct node **backref)
{
struct node *fast, *slow;
slow=source;
fast=source->next;
while(fast!=NULL) {
fast=fast->next;
if(fast!=NULL) {
slow=slow->next;
fast=fast->next;
}
}
*frontref=source;
*backref=slow->next;
slow->next=NULL;
}
When you call the function, use the addresses of the parameters for the second and third argument, so use FrontBackSplit(headref,&a,&b); instead of FrontBackSplit(headref,a,b);.
As far as I can see, SortedMerge() should work (with a modified FrontBackSplit()). It is recursive, but not tail-recursive, so you might have problems with overflowing the stack for long lists. It isn't hard to make iterative, though.
You should make main() either int main(void) or int main(int, char**). You should return 0 for success.
My guess is that one of three things are breaking your code. When you Create() your lists, you do not get the lists you want. In just the right circumstances, with just the right compiler and function call configurations, however, you might get lucky (and maybe that is what you have seen). In that case, it might be the return in MergeSort(). Return head instead, there, that is probably what you want. If you have an empty list or a list of length one, you should return that list. So change return; to return head;. And if it isn't that either, it is probably because you recurse on random data in MergeSort(), because a and b aren't initialised in the recursion. They are uninitialised when you call FrontBackSplit() and the call doesn't change them, because they are passed by value and not reference. The change I listed above will fix that.
There might be more that I have overlooked, but at least those three issues are enough to break the code, each of them on their own, so it is a good place to start with debugging.

Memory leaks in doubly linked list

I'm pretty new to C programming.
I have an assignment in which we are supposed to create a doubly linked list of integers, and write some functions to manipulate them. We are being asked to prevent memory leaks, but I'm not really sure how to do that.
I have to malloc a bunch of times in order to create and store nodes when making the linked list, and I'm pretty sure it's not a good idea to malloc enough space for a node and then free the pointer to it in the same place.
Therefore, my best guess is that I should free all nodes in the main function, when I will have printed their contents to the screen and they are no longer needed. I tried to implement a kill function that takes as input a reference head to the first node in the list, and which iterates over the nodes, freeing them as they go.
I went as far as installing valgrind to try and see if there were any memory leaks, and it looks like there are still some. I have no idea where they are coming from or how to fix the issue.
Here is the whole code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Node{
int data;
struct Node *next;
struct Node *previous;
}Node;
void print_dll(Node *head){
Node *curr = head;
while(curr != NULL){
printf("%d\t", curr->data);
curr = curr->next;
}
puts(" ");
}
Node* create_dll_from_array(int array [], int arrSize){
//this is a function that creates a doubly linked list
//with the contents of the array
Node* current = (Node *) malloc (sizeof(Node * ));
current->data = array[arrSize-1];
current -> next = NULL;
for(int i = 2; i <= arrSize; i++){
//create a new node
Node * temp = (Node*)malloc(sizeof(Node*));
//I would like the dll to be in the same order as the array, I guess it isn't strictly necessary
temp ->data = array[arrSize-i];
temp -> next = current;
current-> previous = temp;
//now make temp the current
current = temp;
}
current-> previous = NULL;
return current;
}
void insert_after(Node* head, int valueToInsertAfter, int valueToInsert ){
if(head != NULL){
Node * current = head;
while(current-> data != valueToInsertAfter){
//this while loop brings 'current' to the end of the list if
//the searched value is not there
if(current-> next != NULL){
current = current->next;
}else{
break;
}
}
//after exiting this loop, the current pointer is pointing
//either to the last element of the dll or to the element
//we need to insert after
Node *new = (Node *) malloc (sizeof(Node *));
new->data = valueToInsert;
new->next = current->next;
new->previous = current;
if(current->next != NULL){
(current->next)->previous = new;
}
current->next = new;
}
}
void delete_element(Node* head, int valueToBeDeleted){
//work in progress
}
void kill(Node *head){
//this is my attempt at freeing all the nodes in the doubly linked list
Node *current;
while(head!=NULL){
current = head;
head = head->next;
free(head);
}
}
int main(){
int array [5] = {11, 2, 7, 22, 4};
Node *head;
/*Question 1*/
//creates a doubly linked list from the array below
head = create_dll_from_array(array, 5); ///size of the array is 5
/* Question 2 */
// print_dll(head);
/*Question 3*/
// to insert 13 after the first appearance of 7
insert_after(head, 7, 13);
print_dll(head);
//to insert 29 after first appearance of 21
insert_after(head, 21, 29);
print_dll(head);
/*Question 6*/
//create a function to free the whole list
kill(head);
return 0;
}
The main function here is given to us by the prof, we have to build out function around it.
I don't know why this is still appearing to lead to memory leaks, and if I', being honest, I don't really know where else they could occur. As far as I know, I need to keep all the memory until almost the last minute.
Please help, I'm pretty lost here.
Thank you!
There are two problems:
Need to change all malloc (sizeof(Node*)) to malloc (sizeof(Node))
Need to change free(header) to free(current) in the kill function.
The modified code is as follows
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
struct Node *previous;
} Node;
void print_dll(Node *head)
{
Node *curr = head;
while(curr != NULL) {
printf("%d\t", curr->data);
curr = curr->next;
}
puts(" ");
}
Node *create_dll_from_array(int array [], int arrSize)
{
//this is a function that creates a doubly linked list
//with the contents of the array
Node *current = (Node *) malloc (sizeof(Node));
current->data = array[arrSize - 1];
current -> next = NULL;
for(int i = 2; i <= arrSize; i++) {
//create a new node
Node *temp = (Node *)malloc(sizeof(Node));
//I would like the dll to be in the same order as the array, I guess it isn't strictly necessary
temp ->data = array[arrSize - i];
temp -> next = current;
current-> previous = temp;
//now make temp the current
current = temp;
}
current-> previous = NULL;
return current;
}
void insert_after(Node *head, int valueToInsertAfter, int valueToInsert )
{
if(head != NULL) {
Node *current = head;
while(current-> data != valueToInsertAfter) {
//this while loop brings 'current' to the end of the list if
//the searched value is not there
if(current-> next != NULL) {
current = current->next;
} else {
break;
}
}
//after exiting this loop, the current pointer is pointing
//either to the last element of the dll or to the element
//we need to insert after
Node *new = (Node *) malloc (sizeof(Node));
new->data = valueToInsert;
new->next = current->next;
new->previous = current;
if(current->next != NULL) {
(current->next)->previous = new;
}
current->next = new;
}
}
void delete_element(Node *head, int valueToBeDeleted)
{
//work in progress
}
void kill(Node *head)
{
//this is my attempt at freeing all the nodes in the doubly linked list
Node *current;
while(head != NULL) {
current = head;
head = head->next;
free(current);
}
}
int main()
{
int array [5] = {11, 2, 7, 22, 4};
Node *head;
/*Question 1*/
//creates a doubly linked list from the array below
head = create_dll_from_array(array, 5); ///size of the array is 5
/* Question 2 */
// print_dll(head);
/*Question 3*/
// to insert 13 after the first appearance of 7
insert_after(head, 7, 13);
print_dll(head);
//to insert 29 after first appearance of 21
insert_after(head, 21, 29);
print_dll(head);
/*Question 6*/
//create a function to free the whole list
kill(head);
return 0;
}
Change sizeof(Node * ) to sizeof(Node) due to malloc reserving you memory for which the pointer points to and it needs the correct amount of needed memory (which is not a pointer but the object itself).
i <= arrSize might be an overflow, since the size usually is given as amount of memory cells. So you might consider using i < arrSize
The first while loop in the insert_after might point to invalid memory after the array
Node *new = is ugly syntax, since new is a keyword in C++. Please never do that, since that will break any code, which is being used in C++.
You dont need a temporary element in kill(). You can instead going until head points to NULL.
delete_element needs the same array checks as insert_after
Probably you need to debug the whole thing pasting one function after the other to get it properly working. No guarantee for correctness, since that was abit hard to read without comments and all.
The best way to find memory leaks is using valgrind (or a similar tool) in run time.
Valgrind will identify any memory leak or violation you ran through.
to run valgrind in linux environment, all you need to do is:
# valgrind --leak-check=full ./my_program
In you case it gave mainy theses errors:
==28583== Invalid read of size 8
==28583== at 0x400871: kill (aaa.c:77)
==28583== by 0x40092D: main (aaa.c:103)
==28583== Address 0x5204188 is 0 bytes after a block of size 8 alloc'd
==28583== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28583== by 0x40073A: create_dll_from_array (aaa.c:29)
==28583== by 0x4008D9: main (aaa.c:87)
this error means the allocation size was too small. as mentioned in another answers it is because you allocate enough memory for a pointer and not for the struct.

Wouldn't the function crash if i pass head to be null and k to be any positive integer?

the following is a function that reverses elements of a link list k elements at a time,
My question is whether the function could crash if i were to pass null as head, because, next is never initialized to any value in this case and since it may be pointing to a garbage value, the if(next!= null) may be satisfied, so the statement head->next can be executed, when head is actually null, causing the program to crash?
struct node *reverse (struct node *head, int k)
{
struct node* current = head;
struct node* next;
struct node* prev = NULL;
int count = 0;
/*reverse first k nodes of the linked list */
while (current != NULL && count < k)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
count++;
}
/* next is now a pointer to (k+1)th node
Recursively call for the list starting from current.
And make rest of the list as next of first node */
if(next != NULL)
{ head->next = reverse(next, k); }
/* prev is new head of the input list */
return prev;
}
If you pass head as NULL, you skip the while loop, and the statement if(next != NULL)would compare an uninitialized pointer to NULL, which is undefined behavior.
So yes, your program could crash.
What an unitialized pointer contains is not specified, and is implementation dependent.
The strict answer to your quesion is: no, not likely.
Given head == 0 and k > 0, you'll end up at line 21 in your function with next != 0 (most likely) but with some garbage random value (as you pointed out) that will be used for your recursive call.
Your code may very well not crash the first time through, but it's very likely to crash at some point, or continue to recurse for some unknown time. In any event, you won't get the results you expect.

doubly linked list insert at middle

Can anyone identify what is happening in my code that is causing the segmentation fault? Please modify/correct the wrong part.
void InsertAtMid (Node *head){
int num,count=0,i;
Node *ptr=head;
Node *newnode=NULL;
Node *newnode2=head;
printf("Enter node to be inserted: ");
scanf("%d", &num);
if (head==NULL){
newnode = head;
newnode=(Node *)malloc(sizeof(Node));
newnode->x=num;
newnode->next=NULL;
newnode->prev=NULL;
} else {
ptr=head->next;
while(ptr->x!=(count/2)){
ptr=ptr->next;
}
newnode->next=ptr->next;
newnode->prev=ptr;
ptr->next->prev=newnode;
ptr->next=newnode;
}
}
So, based on my understanding of your code - the following should [mostly] work:
void InsertAtMid (Node **head){
int num = 0;
int count = 0
int advance = 0;
Node *ptr = *head;
Node *newnode = NULL;
printf("Enter node to be inserted: ");
scanf("%d", &num);
if (*head == NULL) {
*head = (Node *)malloc(sizeof(Node));
ptr = *head;
ptr->x = num;
ptr->next = NULL;
ptr->prev = NULL;
} else {
// *** Count the number of items
ptr = *head;
while (ptr != NULL) {
ptr = ptr->next;
count++;
}
// *** Move to the middle of the list
ptr = *head;
while (advance < (count/2)){
ptr = ptr->next;
advance++;
}
// *** Insert the new value
newnode = (Node *)malloc(sizeof(Node));
newnode->x = num;
newnode->next = ptr->next;
newnode->prev = ptr;
ptr->next->prev = newnode;
ptr->next = newnode;
}
}
The following are the issues I fixed:
You are assigning to head at one point, but since "head" isn't passed in as a reference, the value isn't going to be maintained beyond the first time the function is called. Needless to say you need a pointer to a pointer of type node.
You never calculated the number of items in the list. Often "head" pointer would store this information and you would increment when you add a node, but since you don't have that the only way to determine it is to traverse the list till you find the count.
You never allocated space for the new node to insert except if you were initializing the head pointer. This was also an issue.
Hope that helps some. Best of luck!
int num,count=0,i;
...
ptr=head->next;
while(ptr->x!=(count/2)){
ptr=ptr->next;
count is initialized to 0 and never changed.
So unless you enter "0" for x, that while loop is just going to walk off the end of the list, every time.
Test to work out under what circumstances your code segfaults.
You'll find that it works OK when head == NULL, but fails if head is not null.
So you know that your error is somewhere in the else block.
Step through your running code in a debugger (if you don't know how, it's never too early to learn: whenever you solve a problem with a debugger, you think "why didn't I turn to this sooner?").
Work out what you expect to happen, watch the variables in the debugger, and when the actual values deviate from your expectations, reason about why.
It's not clear to me what you expect to happen in your code, but what will actually happen for a list with one node, is that:
it will execute ptr=head->next; -- so ptr is now NULL.
then for the while condition it will try to dereference ptr->x, and since ptr is NULL, it will segfault.
A quick fix for that would be:
while(ptr != NULL && ptr->x ....) {
But you need to think about whether that's the actual logic you want; and once you get past that, you'll hit other problems (for example, count never changes), which can be sorted out with a debugger in the same way.

How to check if free(node) works

Here is the code for freeing the whole linked list
void free_list(RecordType *list)
{
RecordType *tempNode; /* temporary Node to hold on the value of previous node */
while(list != NULL) /* as long as the listnode doesn't point to null */
{
tempNode = list; /* let tempNode be listNode in order to free the node */
list = list->next; /* let list be the next list (iteration) */
free(tempNode); /* free the node! */
}
}
I think this code itself is working ok (?), but I have no idea how to check.
I only applied the theory (e.g. # of frees must = to the # of mallocs)
So here are some questions that I'm wondering...
Does this method work?
Do I need to malloc tempNode?
I initialized tempNode before while loop... but after I free, tempNode still works... I don't really get that part
The theory that I used:
# of free() == # of malloc()
You need a temporary node to hold the current node
Let the current node equal to the next node
Free the current node by using the temporary node
If any of my theory sounds wrong, please explain!
Thanks!
Does this method work?
Yes, assuming the list nodes were all dynamically allocated and haven't been previously freed
Do I need to malloc tempNode?
You don't need to allocate any memory inside free_list but all list elements must have been dynamically allocated previously. You can only call free on memory that was allocated using malloc (or calloc)
I initialized tempNode before while loop... but after I free, tempNode
still works... I don't really get that part
Calling free returns ownership of memory to the system. It may choose to reuse this memory immediately or may leave it untouched for some time. There's nothing to stop you accessing the memory again but the results of reading or writing it are undefined.
If you want to make it harder for client code to accidentally access freed memory, you could change free_list to NULL their pointer
void free_list(RecordType **list)
{
RecordType *tempNode;
while(*list != NULL) {
tempNode = *list;
list = tempNode->next;
free(tempNode);
}
*list = NULL;
}
If you also want to check that you really have freed all memory, look into using valgrind. This will report any memory leaks and also flags some types of invalid memory access.
The method certainly works - but it should be mallocd first before freeing. Otherwise it is undefined behavior.
You don't need to malloc() tempNode only if list has been previously malloc()d.
The third part is undefined behavior. After free() the data may still exist, but is flagged for being overwritten. You cannot rely on the node once it is free()d
The best way to check your code is interactive tracing by means of Debugger. Gdb in KDevelop on Linux or MS Visual Studio's debugger on MS Windows are perfect. I'll use the later for this demonstration.
This code defines a uni-directed list of integers with three functions: ListPush() adds an integer to the list, ListPrint() displays the list contents and ListDestroy() destroys the list. In main() I insert 3 integers into the list, print them and destroy the list.
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct Node NODE, *PNODE;
typedef struct Node {
int item;
PNODE next;
};
PNODE ListPush(PNODE head, int item) {
PNODE p;
PNODE n = (PNODE) malloc(sizeof(NODE));
if ( !n ) exit(1);
n->next = 0;
n->item = item;
if (!head) {
head = n;
}
else {
for ( p=head; p->next != 0; p=p->next );
p->next = n;
}
return head;
}
void ListPrint(PNODE head) {
PNODE p;
printf("List contents:\n\n");
for (p=head; p!=0; p=p->next) {
printf("%d ", p->item );
}
}
void ListDestroy( PNODE head ) {
PNODE n, c = head;
if ( !head ) return;
do {
n = c->next;
free(c);
c = n;
} while (c );
}
int main() {
int i;
int a[3] = {1,2,3};
PNODE head = 0;
for ( i = 0; i<3; ++i ) {
head = ListPush(head, a[i]);
}
ListPrint(head);
ListDestroy(head);
return 0;
}
Three attached images illustrate 2 stages of the program (MSVS2012 Debugger).
The first shows state of relevant local vars after for() cycle finishes. Look at head variable and proceed on the tree. You can see three nodes with their contents: integers 1,2 and 3 respectively.
The second image shows the variables inside ListDestroy() after first call to free(). You can see that head points to freed memory (red circles) and pointer in variable c points to the next node being destroyed on the next loop.

Resources