I am programming an ADT in c++ using linked lists, and I keep getting a segmentation fault from what appears to be a while loop in one of my functions. I've tried to debug it myself but can't find the source of the fault.
Here is the code in the function:
void remove(int a){
cout << "remove" << endl;
node * temp;
cout << "remove 2" <<endl;
while(head->number != a){
cout << "remove 3" << endl;
head = head->next;
}
cout << "remove 4" << endl;
temp = head;
head = head->next;
delete temp;
}
Head is a normal node, where the variable number in head is an integer. Head itself is a node pointer.
Thanks!
As everyone has pointed out in the comments, you need to check if you have reached the end of the list by checking for the value of head.
void remove(int a){
node * temp;
node *ptr = head;
/* Keep iterating list if {Conditional 1 - Head is not NULL && Conditional 2 - number not equal to a} */
while(ptr && ptr->number != a){
ptr = ptr->next; //Walk the linked list
}
/* Do this only if head is not NULL!! */
if(ptr != NULL) {
/* Save head pointer in temp */
temp = ptr;
/* Increment head to point to next node in list */
ptr = ptr->next;
/* Delete original item pointed to by temp */
delete temp;
}
}
Ideally 'head' always points to a beginning of the list (hence the name head). We usually declare a temporary pointer that points to head before iterating the list.
You have to check that head does not point to null before using number
Related
So basically I have this assignment on my University that asks to make a sorted singly linked list and then make some methods on it. The one that I'm having trouble is: "create delete() function that checks the average of each triple elements and if it's lower than integer 'K' (which is a parameter of said function) deletes the first element of the triple or deletes second and last element of the triple if it's higher."
I already made a function/method that deletes a single element of the linked list.
void LinkedList::deleteElement(int a)
{
Node *temp = head;
Node *previousTemp = head;
while(temp != nullptr)
{
if(temp->value == a)
{
break;
}
else
{
previousTemp = temp;
temp = temp->next;
}
}
if(temp == nullptr)
{
cout << "Can't delete. Element not found." << endl;
}
else
{
cout << "\nDeleting element: " << temp->value << endl;
previousTemp->next = temp->next;
delete temp;
}
howMany--;
}
void Sznur::deleteTriple()
{
Node *first = head;
Node *second = first->next;
Node *third = second->next;
}
The task is written pretty hard to understand but for ex.:
int K=3
linkedList: 7,6,6,3,3,3,2,1,1,1,1
after running the function:
linkedList: 7,3,1,1,1,1
(7+6+6)/3 > K -> deletes 6 and 6
(3+3+3)/3 > K -> deltes second 3 and last 3
(2+1+1)/3 < K -> deletes 2
If the linkedList length is not dividable by 3 the last elements stay in their place.
Try something like this.
void tripleFunc(Node* head, int K)
{
Node* nodePtr = head; // nodePtr always points at the start of a new triple
while (true)
{
Node* first = nullptr;
Node* second = nullptr;
Node* third = nullptr;
first = nodePtr; // When taking the three elements out, remember to always check for a null pointer BEFORE accessing the element
if (first)
second = first->next;
if (second)
third = second->next;
if (third)
nodePtr = third->next; // Keep the nodePtr pointing at the start of the next triple
else
return; // Only happens if one or more of the previous ifs failed, which means that we don't have enough elements left for a full triple
if (calculateAverage(first, second, third) < K) // Make this function
{
deleteElement(first->value);
}
else
{
deleteElement(second->value);
deleteElement(third->value);
}
}
}
I haven't tested it though, so any possible bugs are left as an exercise to the reader to find and sort out. :)
List.H
void List::pop_back()
{
if (size == 0)
cout << "The list is empty and there is no node to pop off the back of the list" << endl;
else if (size == 1)
{
delete tail;
head = tail = iterator = NULL;
}
else
{
NodeRef temp = tail;
while (iterator->next != NULL)
iterator = iterator->next;
tail = iterator;
delete temp;
size--;
}
}
void List::begin() //Set the iterator to the head of the list
{
iterator = head;
}
void List::push_front(int data) //Inserting a new node in the front of the list
{
if (size == 0) //If there is no nodes in the list, execute the if statement
{
head = new Node(data); //create a new node, and have head point to it
tail = head; //have tail point to the new node also.
}
else //If there are nodes in the list, execute the else statement
{
NodeRef newNode = new Node(data); //create a new node
newNode->next = head; //have the next pointer point to the head of the next node.
head = newNode; //have the head pointer point to the new node inserted at the beginning of the list
}
size++; //Increment the size counter
}
void List::print()
{
iterator = head; //Have iterator point to the head
if (size == 0)
cout << "There is nothing in the list" << endl;
else
{
while (iterator!= NULL)
{
cout << iterator->data << endl; //Display contents in node
iterator = iterator->next; //Move to the next node;
}
}
}
List.cpp
int main()
{
List B;
B.push_front(5);
B.push_front(4);
B.push_front(3);
B.begin();
B.pop_back();
B.print();
return 0;
}
So the issue I am having is that after the pop_back() function is called and I call the print() function. It has the last node popped but there is a junk number at the end of the list. I believe what is going on is that it is displaying the final node Next* which is an address. I know it has something to do with the else portion of the pop_back() and iterator->next causing it to point to an address.
You have two issues:
The else condition of your pop_back() function - You iterate through your list to get to the end, and you correctly release the memory of the last node, but you don't make the next pointer of the new tail point to NULL. The new tail's next pointer is still pointing to some random piece of unallocated memory and is actually a bug.
You aren't actually moving the tail pointer. Your inner while() loop just gets iterator to point to the same tail as before.
Try changing your else statement in pop_back() to:
iterator = head;
while (iterator->next->next != NULL) {
iterator = iterator->next;
}
tail = iterator;
delete tail->next;
tail->next = NULL;
--size;
This code assumes the size of your list is at least 2 (the above if statements take care of 0 and 1, so this should be fine for this code sample).
I am trying to output the elements from each node starting from right (rear/tail) to left (head/front). However, my program enters an infinite loop that displays the same element over and over again. Despite its infinite loop, the function that I created DisplayFromLeftToRight() (found below the DisplayFromRightToLeft() function) works like a charm, but this doesnt...
void DisplayFromRightToLeft()
{
node *newnode = rear;
int num = 1;
while (newnode != NULL)
{
cout << "Node # " << num << ": " << newnode->data << endl;
newnode = newnode->previous;
num++;
}
return;
}
This is the working code for printing element from each node from LEFT to RIGHT..
void DisplayFromLeftToRight()
{
node *newnode = front;
int num = 1;
while (newnode != NULL)
{
cout << "Node # " << num << ": " << newnode->data << endl;
newnode = newnode->next;
num++;
}
return;
}
If you believe that my DisplayFromRightToLeft() function is correct, I assume that the problem is from the INSERT function, take a look:
void INSERT(int _data)
{
node *newnode = new node;
newnode->data = _data;
newnode->next = NULL;
newnode->previous = newnode;
rear = newnode;
node *index = new node;
index = front;
if (isEmpty())
front = newnode;
else
{
while (index->next != NULL)
{
index = index->next;
}
index->next = newnode;
}
}
Looks like INSERT always sets previous to a valid address, so your while (newnode != NULL) loop will never end, because it sets newnode = newnode->previous each time.
You are correct that the problem seems to be in your INSERT function, and/or any other code that determines what is in your list.
Your Display...() functions look OK as far as not looping, by themselves, but by making them rely on a while(someNode != NULL) loop, they are dependent on the assumption that the list data is well-formed. Specifically, the first node must have a previous pointer to NULL, and the last node must have a next pointer to NULL, or else one of those Display functions will loop until it finds a NULL.
Looking at the INSERT function, if you follow the steps, you will see that it assigns a new valid address to newnode, and then sets previous to that valid address, while next gets NULL, and it can assign next to something else. How would previous ever be NULL? Previous also points to the inserted node itself, which looks like any loop following previous is just going to keep looking at the same node.
Whenever working with linked lists that I implement, I always step through the code and diagram what is going on - on paper is best, rather than just imagination. Only draw things you actually have your code do. Otherwise, you are probably assuming the resulting list data is what you intended, and not what you've actually coded.
You should change:
newnode->previous = newnode;
To:
newnode->previous = rear;
Line:
newnode->previous = newnode;
has no sense. It is like saying that I am my own father.
I realize this might be a simple problem but I'm not catching it. My requirement is that I have to "4) Delete the linked list and print the list out again. (making sure its gone from memory)"
So I call the delete function and stepping through it, head should be set to NULL when the function finishes.
/*----------------------------------------------------------------------------
* Function: deleteList
* Purpose: delete a link list
* Arguments: head - a pointer to the first node in the linked list
* Returns: N/A
------------------------------------------------------------------------------*/
void deleteList(node *head)
{
struct node *temp;
// Loop through the list, deleting one node at a time.
while(head != NULL)
{
temp = head->next;
delete(head);
head = temp;
}
}
So when I call the print function and send in head, it should be NULL and catch on the first if statement and return to main. But it is bombing out instead.
/*----------------------------------------------------------------------------
* Function: printList
* Purpose: prints a link list
* Arguments: head - a pointer to the first node in the linked list
* Returns: N/A
------------------------------------------------------------------------------*/
void printList(node *head)
{
if (head == NULL)
{
cout << "Empty List!\n";
return;
}
struct node *temp;
temp = head;
// Loop through the list, printing one node at a time.
while(temp->next != NULL)
{
cout << temp->next->element << endl;
temp = temp->next;
}
}
Now if I use the following in main it works fine. But I'd like to know what small thing im missing in the print function. Been banging my head on this for a while now so I'd thought I would step back and ask for a little guidance.
int main()
{
....
cout << "\n***************** DELETE LIST & PRINT ********************\n";
deleteList(head);
cout << head->element << endl; // This works and shows the list is empty.
//printList(head); // This bombs the program.
....
}
Thanks much.
Node is defined below:
struct node
{
string element;
struct node *next;
};
Declaring head in main:
struct node *head;
//creating head node.
if ((head=new(node)) == NULL)
{
cout << "Error: Could not create head node.\n";
return 1;
}
head->next = NULL;
When you use in main
deleteList(head);
A copy of the pointer "head", pointing to the same place, is passed as parameter.
So, if you change the variable that "head" points to, e.g.:
delete(head);
this will be visible in main(). But when you update the pointer "head" itself, e.g.:
head = temp;
The only pointer updated is the one in the scope of the function, and not the one in main. So now your "head" pointer in main points to a deleted variable.
To solve the issue, you could return the new place that "head" should point to, like so:
node *deleteList(node *head)
{
struct node *temp;
// Loop through the list, deleting one node at a time.
while(head != NULL)
{
temp = head->next;
delete(head);
head = temp;
}
return head;
}
And call it with
head = deleteList(head);
I wanted to test the following code (which works fine for a non-null list) to see what would happen in the case of an empty list (in which case the head would be null).
hence the code which applies to filling the list is commented out..
But for some strange reason, the test for NULL in print_nodes() just doesnt seem to work. ive added some debug cout calls to see (and also checked using gdb) but whilst the value does indeed appear to be NULL, any if statements dont seem to test the equivalence properly..
any idea why?
many thanks!
#include <iostream>
using namespace std;
struct node {
char dat;
node *nextPtr;
};
//inserts new node and returns pointer
node* new_node(char data, node* prevNode);
//adds a new node at the head ofthe list
void new_head (node *head_, char dat_);
//inserts new node after *before
void insert_node (node *before, char dat_);
//runs through and prints the list - requires first node (head)
void print_nodes (node *head);
int main() {
cout <<endl << endl;
cout << endl << "*******************RUN******************" <<endl <<endl;
node* head = NULL;
if (head == NULL) {
cout << "head null"; //this works here
}
//head non-standard
// node* head = new node;
// head->dat ='a';
/*
node* b = new_node('b', head);
node* c = new_node('c', b);
node* d = new_node('d', c);
node* e = new_node('e', d);
node* f = new_node('f', e);
*/
print_nodes(head);
insert_node(head,'N');
print_nodes(head);
cout << endl << "*******************END RUN******************" <<endl;
return 0;
}
node* new_node(char data, node* prevNode) {
node* tempPtr = new node;
tempPtr->dat = data;
tempPtr->nextPtr = NULL; //standard
prevNode->nextPtr = tempPtr;
return tempPtr;
}
void new_head (node *head_, char dat_) {
}
void insert_node (node *before, char dat_) {
node* tempPtr = new node;
tempPtr->dat = dat_;
tempPtr->nextPtr = before->nextPtr;
before->nextPtr = tempPtr;
}
void print_nodes (node *head) {
node* tempPtr = head;
cout << "\nPrinting nodes..." <<endl;
if (tempPtr == NULL) { //this test is not working.. why?
cout << "tempPtr is NULL";
return;
} else { //only run in the non null case
for (tempPtr; tempPtr != NULL; tempPtr = tempPtr->nextPtr) {
cout << "Current node content: " << tempPtr->dat <<endl;
}
}
}
You have a problem: head was not allocated, but insert accesses its "next element":
before->nextPtr = tempPtr;
head is passed in as before, and you didn't allocate memory for head. Hence you dereference a NULL pointer here.
Could it be that your application crashes as a result, and the printout to cout isn't done because cout is buffered?
Try to:
Remove the call to insert
Change cout to cerr (unbuffered)
Report the results of these changes.
allocate head before insertion :
node * head = new node;
memset(head, 0, sizeof(node));
The code works for me using g++ 4.4.1 on windows. The message is displayed and then it crashes, because of other issues in the code. You are probably not seeing the message because the crash occurs before the output buffer containing the message is flushed.
In general, it is a good idea to write diagnostic messages to standard error (cerr) rather than standard output, as the error stream is not buffered.