I'm going to be upfront and apologize for asking for your help. I really don't deserve it in any way. I also apologize for what I know is my sloppy code. I don't want to be that guy, but I'm really stumped as to what I should do.
Here's the story so far:
list.cpp
Node::Node()
{
next = nullptr;
book = nullptr;
}
Node::Node(Book* newBook)
{
next = nullptr;
book = newBook;
}
Node::~Node()
{
if (next != nullptr)
{
delete book;
}
}
Node* Node::getNextNode() const
{
return next;
}
void Node::setNext(Node* newNext)
{
if (newNext == nullptr)
{
next = nullptr;
}
else
{
next = newNext;
}
}
Book* Node::getBook() const
{
return book;
}
List::List()
{
first = nullptr;
last = nullptr;
numNodes = 0;
}
List::~List()
{
Node* tempNode = first;
first = first->getNextNode();
while (first != nullptr)
{
delete tempNode;
numNodes--;
tempNode = first;
first = first->getNextNode();
}
last = nullptr;
}
void List::push_front(Node* newNode)
{
Node* tempNode = first;
newNode->setNext(tempNode);
first = newNode;
if (last == nullptr)
{
last = first;
}
numNodes++;
}
void List::push_back(Node* newNode)
{
Node* tempNode = last;
tempNode->setNext(newNode);
last = newNode;
if (first == nullptr)
{
first = last;
}
numNodes++;
}
Node* List::pop_front()
{
if (first != nullptr)
{
Node* tempNode = first;
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
first = first->getNextNode();
numNodes--;
return tempNode;
}
return nullptr;
}
Node* List::pop_back()
{
if (last != nullptr)
{
Node* tempNode = first;
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
while (tempNode->getNextNode()->getNextNode() != nullptr)
{
tempNode = tempNode->getNextNode();
}
last = tempNode;
last->next = nullptr;
numNodes--;
return tempNode;
}
return nullptr;
}
Node* List::getFirst() const
{
return first;
}
Node* List::getLast() const
{
return last;
}
driver.cpp
int main()
{
// set up cout for displaying prices
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
// create a List object
List partsList;
cout << "\nPart I: multiple node test: push_front and pop_front\n";
cout << "\n----------------------------------\n";
// build a List using push_front
partsList.push_front(new Node(new Book("Fun With C++", "I. M. Codemann", 95.00)));
partsList.push_front(new Node(new Book("Lousy Linked Lists", "B. A. Hacker", 74.90)));
partsList.push_front(new Node(new Book("Programming Nuts", "R. U. Krazy", 85.25)));
partsList.push_front(new Node(new Book("Silly Syntax", "Irma Coder", 30.15)));
cout << "\nThe original nodes in the List:\n";
printList(partsList);
cout << "\n----------------------------------\n";
// test push_front function
cout << "\nAdding to the front of the List:\n";
cout << "\n----------------------------------\n";
partsList.push_front(new Node(new Book("Python's a Snake", "C. Rules", 65.45)));
partsList.push_front(new Node(new Book("Programming Fables", "J. Aesop", 73.15)));
printList(partsList);
cout << "\n----------------------------------\n";
// test pop-front
cout << "\nRemoving the first node from the list.\n";
cout << "\n----------------------------------\n";
Node* item = partsList.pop_front();
printList(partsList);
if (item != NULL)
delete item;
cout << "\n----------------------------------\n";
cout << "\nPart Two: Push_back and pop_back";
// test push_back
partsList.push_back(new Node(new Book("Coding Shortcuts", "B. Lazy", 110.25)));
partsList.push_back(new Node(new Book("Famous Programmers I Know", "M. T. Set", 126.00)));
cout << "\nAdding two nodes at the end\n";
cout << "\n----------------------------------\n";
printList(partsList);
// test pop-back
cout << "\n----------------------------------\n";
cout << "\nRemove last node from the list\n";
cout << "\n----------------------------------\n";
item = partsList.pop_back();
printList(partsList);
if (item != NULL)
delete item;
// delete all of the Nodes in the list
cout << "\nEmpty the list and delete all nodes\n";
while (partsList.getFirst() != nullptr)
{
Node * t = partsList.pop_front();
delete t;
}
printList(partsList);
// Test Push_front and pop_back - do they handle special case
cout << "\nTesting special case handling for push_front and pop_back\n";
partsList.push_front(new Node(new Book("Test Book 1", "nobody", 1.25)));
Node* t = partsList.pop_back();
cout << "\nThe Node just removed contains " << t->getBook()->getTitle() << endl;
delete t;
// Test push_back and pop_front - do they handle special cases
cout << "\nTesting special case handling for push_back and pop_front\n";
partsList.push_back(new Node(new Book("Test Book 2", "nobody", 1.25)));
t = partsList.pop_front();
cout << "\nThe Node just removed contains " << t->getBook()->getTitle() << endl;
delete t;
// Is the list now empty
cout << "\nThe list should now be empty...\n";
printList(partsList);
cout << "\n-------------------------------------------\n";
cout << "\nEnd of Test";
cout << "\n-------------------------------------------\n";
system("PAUSE");
return 0;
}
void printList(const List& theList)
{
if (theList.getFirst() == nullptr) // if the list is empty
cout << "\nempty list\n";
else
{
Node* t = theList.getFirst();
while (t != NULL)
{
Book* bp = t->getBook();
cout << bp->getTitle() << ' ';
cout << bp->getAuthor() << ' ';
cout << "$" << bp->getPrice() << endl;
t = t->getNextNode();
}
}
}
void printFirstNode(List theList)
{
Node* t = theList.getFirst();
Book* bp = t->getBook();
cout << bp->getTitle() << ' ';
cout << bp->getAuthor() << ' ';
cout << "$" << bp->getPrice() << endl;
t = t->getNextNode();
}
Alright, so what I have so far for my linked list seems to run relatively smoothly so far. The program only throws an exception (Unhandled exception at 0x00E5654B in Proj_02.exe: 0xC0000005: Access violation reading location 0xDDDDDDF1.) at the homestretch, the last printList function call. I'm not really sure why this exception is being thrown, the list should be completely empty. And so, I must hang my head in shame. What should I do to fix this issue?
Thank you. From the bottom of my heart.
Thomasz' answer is right so far, just some additional hints:
Wherever you set one of first or last to nullptr, do it for the other one, too:
first = last = nullptr;
push_back won't work, if the list is empty, last is nullptr itself then. Try this instead:
if(!last)
{
first = last = newNode;
}
else
{
last->setNext(newNode);
last = newNode;
}
++numNodes;
The Node class should be an internal detail of your list class, else you allow to modify the list externally without the List knowing about it! Make Node an inner class, and change the List's public interface such that it handles a Book* wherever you had a Node* before. Create and delete the Nodes within the List's functions then - and do not delete the Book from within the Node's destructor. You can still use the Node class to iterate the List, though, so it would correspond to the ::std::list's iterator.
And you can assign newNext directly: even if it is a null pointer, next still will be assigned exactly this, so the check is simply superfluous:
void Node::setNext(Node* newNext)
{
// if (newNext == nullptr)
//{
// next = nullptr;
//}
//else
//{
next = newNext;
//}
}
May I assume that you wrote the list for learning? Otherwise, you would probably have used ::std::list from the STL...
I allowed myself to rewrite your list class so you can compare. Hope this helps you...
class List
{
public:
List();
~List();
class Node
{
public:
Node const* getNext() const
{
return next;
}
Book* getBook() const
{
return book;
}
private:
friend class List;
Node(Book*);
Node* next;
Book* book;
};
void push_front(Book* book);
void push_back(Book* book);
Book* pop_front();
Book* pop_back();
Book* getFirst() const;
Book* getLast() const;
Node const* getFirstNode() const
{
return first;
}
private:
Node* first;
Node* last;
unsigned int numNodes;
};
List::Node::Node(Book* newBook)
: next(nullptr), book(newBook)
{
}
List::List()
: first(nullptr), last(nullptr), numNodes(0)
{
}
List::~List()
{
while (first)
{
Node* tempNode = first;
first = first->next;
delete tempNode;
}
}
void List::push_front(Book* book)
{
Node* node = new Node(book);
if (first)
{
node->next = first;
first = node;
}
else
{
first = last = node;
}
++numNodes;
}
void List::push_back(Book* book)
{
Node* node = new Node(book);
if (last)
{
last->next = node;
last = node;
}
else
{
first = last = node;
}
++numNodes;
}
Book* List::pop_front()
{
Book* book = nullptr;
if (first)
{
book = first->book;
Node* tempNode = first;
first = first->next;
if (!first)
{
last = nullptr;
}
delete tempNode;
--numNodes;
}
return book;
}
Book* List::pop_back()
{
Book* book = nullptr;
if (last)
{
book = last->book;
Node* tempNode = first;
if (first == last)
{
first = last = nullptr;
}
else
{
while(tempNode->next != last)
{
tempNode = tempNode->next;
}
delete last;
last = tempNode;
last->next = 0;
}
--numNodes;
}
return book;
}
Book* List::getFirst() const
{
return first ? first->book : nullptr;
}
Book* List::getLast() const
{
return last ? last->book : nullptr;
}
Your crash seems to be related to this part of code:
Node* List::pop_front()
{
if (first != nullptr)
{
Node* tempNode = first;
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
first = first->getNextNode();
numNodes--;
return tempNode;
}
return nullptr;
}
Both cases:
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
Seem to serve the purpose of a single-element list (first == last and next == nullptr). So, the first one should be:
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
first = nullptr;
return tempNode;
}
Additionally this:
Node::Node(Book* newBook)
{
next = nullptr;
book = newBook;
}
Node::~Node()
{
if (next != nullptr)
{
delete book;
}
}
is a 100% leak in case of invocation like:
new Node(new Book("Test Book 1", "nobody", 1.25))
since next is still a nullptr, and book should be owned by Node, but isn't. Also, the last element will always leak.
Related
I created a doubly-linked list in C++. Everything works great if I insert a node at the beginning of the list or if I just insert a node at the end of the list; but, when I insert a node at the beginning and then try to insert a node at the end, I get a null pointer error!
Here is the code and the problem is in the InserAtEnd function
#include <iostream>
using namespace std;
struct Node {
int data;
Node* prev;
Node* next;
};
struct MyList {
Node* head;
Node* tail;
};
bool IsEmpty(MyList list) {
if (list.head == nullptr) return true;
else return false;
}
void Insert(MyList& list, int data) {
Node* node = new Node();
node->data = data;
node->prev = nullptr;
node->next = list.head; //node->next points to NULL
if (list.head == nullptr) {
list.head = node;
node->prev = nullptr;
}
else {
//insert the new node at the beginning of the list
list.head->prev = node;
list.head = node;
node->prev = nullptr;
}
}
// Insert node at end of list
void InsertAtEnd(MyList& list, int data) {
Node* node = new Node();
node->data = data;
node->next = nullptr;
node->prev = nullptr;
if (list.head == nullptr) { // Empty list
list.head = node;
list.tail = node;
}
else {
list.tail->next = node;
node->prev = list.tail;
list.tail = node;
}
}
//Traverse the list from the head
void PrintAll(const MyList& list) {
Node* temp = list.head;
if (temp == nullptr) {
cout << "list is empty" << endl;
}
else {
while (temp != nullptr)
{
cout << temp->data << endl;
temp = temp->next;
}
cout << "*************************************" << endl;
}
}
Node* Search(const MyList& list, int key) {
Node* temp = list.head;
while (temp != nullptr && temp->data != key)
{
temp = temp->next;
}
return temp;
}
void Delete(MyList& list, int key) {
Node* temp = Search(list, key); //call search()
if (temp != nullptr)
{
if (temp->prev != nullptr)
{
temp->prev->next = temp->next;
}
else
{
list.head = temp->next;
}
if (temp->next != nullptr)
{
temp->next->prev = temp->prev;
}
}
}
int main() {
MyList list;
list.head = nullptr; //initialize the linked-list
list.tail = nullptr;
if (IsEmpty(list))
cout << "List is empty" << endl;
Insert(list, 10);
PrintAll(list);
/*
Insert(list, 20);
Insert(list, 30);
Insert(list, 40);
*/
// Insert at end
cout << "Now insert at end" << endl;
InsertAtEnd(list, 70);
InsertAtEnd(list, 45);
InsertAtEnd(list, 59);
InsertAtEnd(list, 12);
InsertAtEnd(list, 33);
PrintAll(list);
/*
int x = 24;
Node* result = Search(list, x);
if (result == nullptr) cout << "Cannot find " << x << endl;
else cout << "Found " << result->data << endl;
Delete(list, 200);
PrintAll(list);
Delete(list, 10);
PrintAll(list);
Delete(list, 40);
PrintAll(list);
Delete(list, 20);
PrintAll(list);
Delete(list, 30);
PrintAll(list);
*/
return 0;
}
the problem is in the InserAtEnd function
How do you know?
In fact the problem isn't in InserAtEnd but in Insert:
You never set list.tail.
Tip: use Node **tail = &head; for a more efficient MyList and add member initializers and a Constructor.
This is C++ with classes. Use member functions.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
struct Node
{
int x;
Node* next = nullptr;
};
typedef Node* nodeptr;
class L
{
private:
nodeptr head = nullptr;
int lengthCount = 0;
public:
void add(const int data);
void print();
void find(const int data);
void pop(const int data);
void listSize();
};
void L:: listSize()
{
cout << "The size of the link list is: " << lengthCount << endl;
}
void L::add(const int data)
{
lengthCount += 1;
Node* newNode = new Node;
newNode->x = data;
if(head == nullptr)
{
head = newNode;
}
else
{
nodeptr temp = head;
while(temp->next != nullptr)
{
temp = temp->next;
}
temp->next = newNode;
}
}
void L::pop(const int data)
{
if(head == nullptr)
{
cout << "Empty List" << endl;
}
else if(head->x == data)
{
head = head->next;
}
else
{
nodeptr temp = head->next;
while(temp != nullptr)
{
if(temp-> x != data)
{
temp = temp->next;
}
else
{
if(temp->next != nullptr)
{
temp = temp->next;
}
else
{
temp->next = nullptr;
}
break;
}
}
}
}
void L::find(const int data)
{
if(head == nullptr)
{
cout << "Empty List" << endl;
}
else
{
nodeptr temp = head;
for(temp; temp != nullptr; temp = temp->next)
{
if(temp->x == data)
{
cout << "Found" << endl;
break;
}
if(temp->next == nullptr)
cout << "Not Found" << endl;
}
}
}
void L::print()
{
nodeptr temp = head;
string line(20,'-');
cout << "Print list" << endl;
cout << line << endl;
while(temp != nullptr)
{
cout << temp->x << endl;
temp = temp->next;
}
cout << line << endl;
}
int main()
{
vector <int> val;
for(int i = 0; i < 10; i++)
val.push_back(5*i);
cout << "Printing list" << endl;
for(auto i : val)
cout << i << " ";
cout << endl;
L listObj;
cout << "Adding list" << endl;
for(auto i : val)
listObj.add(i);
listObj.print();
listObj.listSize();
listObj.find(15);
listObj.print();
cout << "popping 10" << endl;
listObj.pop(10);
listObj.print();
}
The problem that I am having is, I am not able to modify the actually memory of a linked list while using a class.
Im not sure what did I do wrong.
If adding works, i would say the idea of removing a value should work as well.
the remove function is called pop.
the pop function is not removing the value 10, so i am not sure why.
If it is a function that is not in a class, i would say i need to pass a head pointer with & operator then i can actually modify the value.
Please let me know where did I do wrong.
Thanks
Your pop method is not correct, You also have to link the current node with the previous next. It should be like this
void L::pop(const int data)
{
if(head == nullptr)
{
cout << "Empty List" << endl;
}
else if(head->x == data)
{
nodeptr temp = head;
head = head->next;
delete temp;
}
else
{
nodeptr temp = head->next;
nodeptr prev = head;
while(temp != nullptr)
{
if(temp-> x != data)
{
prev = temp;
temp = temp->next;
}
else
{
if(temp->next != nullptr)
{
prev -> next = temp -> next;
delete temp;
}
else
{
delete temp;
prev -> next = nullptr;
}
break;
}
}
}
}
You need to keep track of the "previous" node of the linked list somehow. There's many ways to do it, but the clearest might be:
void L::pop(const int data) {
if(head == nullptr) {
cout << "Empty List" << endl;
return;
}
if(head->x == data) {
node *temp = head;
head = head->next;
delete temp;
return;
}
int *prev = head;
int *temp = head->next;
while (temp != null) {
if (temp->x != data) {
prev = prev->next;
temp = temp->next;
} else {
prev->next = temp->next;
delete temp;
return;
}
}
}
In addition to the answers given already there's a variant without having to track the previous element all the time:
for(Node* tmp = head; tmp->next; tmp = tmp->next;)
{
if(tmp->next->x == data)
{
// OK, one intermediate pointer we need anyway, but we need it
// only once
Node* del = tmp->next;
tmp->next = tmp->next->next;
delete del;
break;
}
}
The for loop as a little bonus is a bit more compact, but that's just a matter of personal taste.
I am trying to delete the node of specific data. For this, I am using deleteNode function but not able to delete. Please see the code:
#include<iostream>
using namespace std;
class node
{
public:
int data;
node* next;
///constructor for initializing the data-
node(int d)
{
data=d;
next=NULL;
}
};
void addAtEnd(node* &head,int data)
{
if(head==NULL)
{
head=new node(data);
return ;
}
node* temp=head;
while(temp->next!=NULL)
{
temp=temp->next;
}
node* n =new node(data);
n->data=data;
temp->next=n;
n->next=NULL;
return;
}
AddAtTail(node* head,int d)
{
node* ptr=head;
while(ptr->next!=NULL)
{
ptr=ptr->next;
}
node *n=new node(d);
ptr->next=n;
n->next=NULL;
}
AddAtPosition(node* head,int p,int d)
{
///2 3 4 6 7 8 -1
///4th Position-
node*ptr=head;
int jump=1;
while(jump<=p-1)
{
jump++;
ptr=ptr->next;
}
node*n=new node(d);
n->next=ptr->next;
ptr->next=n;
}
/// Delete First node
void deleteFirst(node *&head)
{
head=head->next;
}
///Delete last node;
void deleteLast(node* head)
{
node* ptr=head;
while(ptr->next->next!=NULL)
{
ptr=ptr->next;
}
ptr->next=NULL;
}
**///Delete Specific Node :-**
Here the function starts for deleting the node. I am trying to delete the node that has data 3 I am taking data as input from main function.
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{
ptr=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
void takeInput(node*& head)
{
int d;
cin>>d;
while(d!=-1)
{
addAtEnd(head,d);
cin>>d;
}
}
void print(node* head)
{
while(head!=NULL)
{
cout<<head->data<<"=>";
head=head->next;
}
}
AddAtFront(node* &head,int d)
{
///create new node;
node*n=new node(d);
n->next=head;
head=n;
}
int main()
{
node* head(NULL);
takeInput(head);
print(head);
cout<<endl<<endl<<endl<<"---------- Here The Insertion Process starts at different Positions -----------"<<endl;
cout<<endl<<"Adding at Tail"<<endl;
AddAtTail(head,9);
print(head);
cout<<endl<<"Adding at Position p"<<endl;
int p,d;
cout<<"Enter Position and data :"<<endl;
cin>>p>>d;
AddAtPosition(head,p,d);
print(head);
cout<<endl<<"Adding at Front"<<endl;
cout<<"Enter data to add at front : "<<endl;
cin>>d;
AddAtFront(head,d);
print(head);
cout<<endl<<endl<<endl;
cout<<endl<<"-------------------- NOW LETS PERFORM DELETION ------------------"<<endl;
cout<<endl<<"Deleting first node :"<<endl;
deleteFirst(head);
print(head);
cout<<endl;
cout<<endl<<"Deleting Last node :"<<endl;
deleteLast(head);
print(head);
cout<<endl;
cout<<"deleting specific node"<<endl;
cout<<"Enter data to delete"<<endl;
cin>>d;
deleteData(head,d);
print(head);
cout<<endl;
return 0;
}
Please see DeleteNode function in which I am trying to delete the node.
Why node is not deleting? Here is the function:
**///Delete Specific Node i.e- data :-**
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{
ptr=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
But Node is not deleting.
Your delete... functions are not actually deleting anything. You are just manipulating pointers but are leaking the actual node objects. And you are not taking into account the possibility that the node being deleted is the head node, which requires updating the head to point at the next node.
Also, your functions will crash on an empty list, and deleteLast will crash on a list with fewer than 2 nodes.
And deleteData is not enumerating nodes correctly.
Try something more like this instead:
#include <iostream>
using namespace std;
class node {
public:
int data;
node* next;
///constructor for initializing the data-
node(int d) {
data=d;
next=NULL;
}
};
node* findLast(node *head, node **before) {
if (before) *before = NULL;
node *last = head;
if (last) {
while (last->next) {
if (before) *before = last;
last = last->next;
}
}
return last;
}
node* addAtFront(node* &head, int data) {
node* n = new node(data);
n->next = head;
head = n;
return n;
}
node* addAtEnd(node* &head, int data) {
node *last = findLast(head, NULL);
node* n = new node(data);
if (last) {
last->next = n;
} else {
head = n;
}
return n;
}
node* addAtPosition(node* &head, int p, int d) {
if ((!head) || (p <= 0)) {
return addAtFront(head, d);
}
node *ptr = head;
node *temp;
do {
temp = ptr;
ptr = ptr->next;
}
while ((ptr) && (--p > 0));
node *n = new node(d);
n->next = temp->next;
temp->next = n;
return n;
}
/// Delete First node
void deleteFirst(node* &head) {
if (head) {
node *ptr = head;
head = head->next;
delete ptr;
}
}
///Delete last node
void deleteLast(node* &head) {
node *beforeLast;
node *last = findLast(head, &beforeLast);
if (last) {
if (beforeLast) {
beforeLast->next = NULL;
}
if (head == last) {
head = NULL;
}
delete last;
}
}
///Delete Specific Node
void deleteData(node* &head, int d) {
node *before = NULL;
node *ptr = head;
while (ptr) {
if (ptr->data == d) {
if (before) {
before->next = ptr->next;
}
if (head == ptr) {
head = head->next;
}
delete ptr;
return;
}
before = ptr;
ptr = ptr->next;
}
}
void takeInput(node* &head) {
int d;
if (!((cin >> d) && (d != -1))) return;
node *last = findLast(head, NULL);
node *n = new node(d);
if (last) {
last->next = n;
} else {
head = n;
}
last = n;
while ((cin >> d) && (d != -1)) {
n = new node(d);
last->next = n;
last = n;
}
}
void print(node* head) {
while (head) {
cout << head->data << "=>";
head = head->next;
}
}
int main() {
node* head = NULL;
takeInput(head);
print(head);
cout << endl;
cout << endl << endl;
cout << "---------- Here The Insertion Process starts at different Positions -----------" << endl << endl;
cout << "Adding at End" << endl;
addToEnd(head, 9);
print(head);
cout << endl;
cout << "Adding at Position p" << endl;
int p, d;
cout << "Enter Position and data :" << endl;
if (cin >> p >> d) {
addAtPosition(head, p, d);
print(head);
cout << endl;
}
cout << "Adding at Front" << endl;
cout << "Enter data to add at front : " << endl;
if (cin >> d) {
addAtFront(head, d);
print(head);
cout << endl;
}
cout << endl << endl << endl;
cout << "-------------------- NOW LETS PERFORM DELETION ------------------" << endl << endl;
cout << "Deleting first node :" << endl;
deleteFirst(head);
print(head);
cout << endl;
cout << endl << "Deleting Last node :" << endl;
deleteLast(head);
print(head);
cout << endl;
cout << "deleting specific node" << endl;
cout << "Enter data to delete" << endl;
if (cin >> d) {
deleteData(head, d);
print(head);
cout << endl;
}
cout << "deleting remaining nodes" << endl;
node *ptr = head;
while (ptr) {
node *temp = ptr;
ptr = ptr->next;
delete temp;
}
return 0;
}
That being said, you really should be using std::list (double linked) or std::forward_list (single linked) instead. Let the STL do the hard work for you.
In your delete node function, you are changing the value of the local variable, ptr. But you are not changing the "next" pointer of the node pointing to the one you want to delete. So it stays in the list. The value of your local pointer becomes irrelevant as soon as you leave the function.
This line is the problem: ptr=ptr->next->next;
You need to change its "next" value. Something like (I haven't compiled and tested this; it is a suggestion to point you in the right direction.)
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{ /* pointer->next points to the node you want to delete.
point it instead to the one beyond */
ptr->next=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
You will then have a memory leak, as you are not freeing the memory to the node you are deleting.
You'd need something like:
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{ /* pointer->next points to the node you want to delete.
point it instead to the one beyond,
freeing the memory of the deleted node */
node * pNodeToDelete = ptr->next;
ptr->next=ptr->next->next;
free(pNodeToDelete)
return;
}
ptr=ptr->next;
}
}
I'm writing a program for a homework assignment that creates and manipulates a linked list. I am encountering an "EXC_BAD_ACCESS" error with the Node::SetData function in Node.cpp, as well as with a line in List::Add_End in List.cpp (specifically "current->SetData(data);") and a line in main.cpp for some reason (specifically "// Add_End nodes to the list"). I assume that once the Node::SetData error is fixed, these other errors will resolve themselves.
After searching through Stack Overflow and Google, I cannot determine why this error is occurring. I thought this question (New to C++, "EXC_BAD_ACCESS" error I don't understand) would help, but I'm still having issues.
What coding error(s) have I made?
main.cpp
#include <iostream>
#include <cstddef>
using namespace std;
#include "List.h"
int main()
{
// New list
List list;
Node *answer;
// Add_End nodes to the list
list.Add_End(111);
list.Print();
list.Add_End(222);
list.Print();
list.Add_End(333);
list.Print();
list.Add_End(444);
list.Print();
list.Add_End(555);
list.Print();
// Delete nodes from the list
list.Delete(444);
list.Print();
list.Delete(333);
list.Print();
list.Delete(222);
list.Print();
list.Delete(555);
list.Print();
list.Delete(111);
list.Print();
cout << "Testing Add_Front: and others" << endl;
list.Add_Front(888);
list.Print();
list.Add_Front(999);
list.Print();
list.Add_Front(49);
list.Print();
cout << "Checking find function" << endl;
answer = list.Find(888);
cout << "Value for node returned by find function call with 888 is " << answer->Data() << "." << endl;
cout << "Checking find function" << endl;
answer = list.Find(999);
cout << "Value for node returned by find function call with 888 is " << answer->Data() << "." << endl;
cout << "Checking find function" << endl;
answer = list.Find(49);
cout << "Value for node returned by find function call with 888 is " << answer->Data() << "." << endl;
cout << "Call find function with value not in list." << endl;
answer = list.Find(7);
if (answer == NULL)
{
cout << "returned null pointer since 7 not found" << endl;
}
else
{
cout << "in else of answer == NULL where Value for node returned by find function call with 7 is " << answer->Data() << "." << endl;
}
cout << "testing delete_front: " << endl;
list.Delete_Front();
list.Print();
cout << "testing delete_end: " << endl;
list.Delete_End();
list.Print();
return 0;
}
List.h
#ifndef LIST_H
#define LIST_H
#include <cstddef>
#include "Node.h"
class List
{
private:
Node* head;
public:
List();
void Add_End(int data);
void Delete(int data);
void Delete_Front();
void Add_Front(int data);
void Delete_End();
Node* Find(int data);
void Print();
};
#endif
List.cpp
#include <iostream>
#include <cstddef>
using namespace std;
#include "List.h"
List::List()
{
head = NULL;
return;
}
void List::Add_End(int data)
{
Node* current;
Node* newEnd = new Node();
for (current = head; current != NULL; current = current->Next())
{}
current->SetData(data);
current->SetNext(newEnd);
newEnd->SetData(NULL);
newEnd->SetNext(NULL);
return;
}
void List::Delete(int data) {
/*
FILL IN CODE (will do later)
*/
return;
}
void List::Delete_Front()
{
/*
FILL IN CODE (will do later)
*/
return;
}
void List::Add_Front(int data)
{
Node* newNode = new Node();
newNode->SetData(data);
newNode->SetNext(head);
head = newNode;
return;
}
void List::Delete_End()
{
if (head == NULL)
{
cout << "List has no member so cannot delete end" << endl;
return;
}
// check if one in length
if (head->Next() == NULL)
{
head = NULL;
return;
}
// 2 or greater in length
Node* current;
Node* prev;
prev = head;
for (current = head->Next(); current->Next() != NULL; current = current->Next())
{
prev = current;
}
prev->SetNext(NULL);
return;
}
Node* List::Find(int data)
{
Node* current;
for (current = head; current != NULL && current->Data() != data; current = current->Next())
{}
if(current == NULL)
{
cout << "Did not find " << data << "." << endl;
return NULL;
}
else // found
{
cout << "Found " << data << "." << endl;
return current;
}
}
void List::Print()
{
Node* current;
for (current = head; current != NULL; current = current->Next())
{
cout << current->Data() << " ";
}
cout << endl;
return;
}
Node.h
#ifndef NODE_H
#define NODE_H
class Node
{
private:
int data;
Node* next;
public:
Node();
void SetData(int aData);
void SetNext(Node* aNext);
int Data();
Node* Next();
};
#endif
Node.cpp
#include <cstddef>
#include "Node.h"
Node::Node()
{
this->SetData(NULL);
this->SetNext(NULL);
return;
}
void Node::SetData(int aData)
{
this->data = aData;
return;
}
void Node::SetNext(Node* aNext)
{
this->next = aNext;
return;
}
int Node::Data()
{
return data;
}
Node* Node::Next()
{
return next;
}
While calling current->SetData for the first time (see below) current is NULL and so you get page fault when accessing it (page fault is the error modern OSes give you if you try to access unallocated memory. Under wndows the term usually is Access violation.)
void List::Add_End(int data)
{
Node* current;
Node* newEnd = new Node();
for (current = head; current != NULL; current = current->Next())
{}
current->SetData(data);
current->SetNext(newEnd);
newEnd->SetData(NULL);
newEnd->SetNext(NULL);
return;
}
I managed to correct the code, so I'll explain what I did in case someone else encounters the same problem.
ALTERATION: Before I explain the fix, let me explain a change I made. The last node of the linked list can hold a data value itself, not just NULL (i.e., the last node's data does not need to be NULL, but its next should be NULL), so I thought this would be better. The code reflects this in every location where it matters, such as the List::Add_End(int data) function.
THE FIX: I modified the List constructor to create a head node for the list. So, the linked list will always have at least one node, even if the list is empty. I will explain how the program discerns between empty and nonempty lists later.
Here is the original constructor:
List::List()
{
head = NULL;
return;
}
Here is the new constructor:
List::List()
{
Node* headNode = new Node();
head = headNode;
return;
}
Why make this modification? As far as I can tell, I encountered the EXC_BAD_ACCESS error because the List::Add_End(int data) function tried to manipulate the linked list's head as if it were a node object, when actually it was not. (I believe this is what marom meant in his answer to this question.) This is why I altered the coding such that the list always contains a head node, even when the list is empty.
Discerning between empty and nonempty lists. I altered the Node constructor to set data to the integer -1122334455, instead of NULL like I originally did. So, if the list is empty, then head->Data() (i.e., the head node's data) is -112233455 and head->Next() (i.e., the head node's next) is NULL. The downside to this approach is that it's impossible to have a one-item list containing the integer -1122334455, but I figure this number is likely to be unneeded. As long as the list has at least two items, head->Data() can be -1122334455.
NEW CODE: The rest of the code reflects these modifications. Since I only made significant changes to List.cpp and Node.cpp, I have reproduced only them below. The other three program files are essentially unchanged. FYI, there are many redundant return's and this's that I didn't bother to delete.
List.cpp
#include <iostream>
#include <cstddef>
using namespace std;
#include "List.h"
// -1122334455 is an arbitrary integer that is likely to never be needed by the user
List::List()
{
Node* headNode = new Node();
head = headNode;
return;
}
Node* List::Add_End(int data)
{
// if list is empty (i.e., list has only head node with data == -1122334455 & next == NULL)
if (head->Data() == -1122334455 && head->Next() == NULL)
{
head->SetData(data);
return head;
}
// if list is nonempty
else
{
Node* current;
Node* newEnd = new Node();
for (current = head; current->Next() != NULL; current = current->Next())
{}
current->SetNext(newEnd);
newEnd->SetData(data);
newEnd->SetNext(NULL);
return newEnd;
}
}
void List::Delete(int data)
{
Node* prev;
Node* current;
// if list is empty
if (head->Data() == -1122334455 && head->Next() == NULL)
{
cout << "Cannot delete this datum because list is empty." << endl;
return;
}
// if list contains 1 element
if (head->Data() == data && head->Next() == NULL)
{
head->SetData(-1122334455);
return;
}
else if (head->Data() != data && head->Next() == NULL)
{
cout << "Datum not found in list." << endl;
return;
}
// if list contains 2 or more elements
prev = head;
for (current = head->Next(); current->Data() != data && current->Next() != NULL; current = current->Next())
{
prev = prev->Next();
}
if (current->Data() == data && current->Next() != NULL)
{
prev->SetNext(current->Next());
delete current;
return;
}
else if (current->Data() == data && current->Next() == NULL)
{
prev->SetNext(NULL);
delete current;
return;
}
else
{
cout << "Datum not found in list." << endl;
return;
}
}
void List::Delete_Front()
{
Node* origHead = head;
Node* newHead = head->Next();
head = newHead;
delete origHead;
return;
}
void List::Add_Front(int data)
{
// if list is empty
if (head->Data() == -1122334455 && head->Next() == NULL)
{
head->SetData(data);
return;
}
// if list is nonempty
Node* newNode = new Node();
newNode->SetData(data);
newNode->SetNext(head);
head = newNode;
return;
}
void List::Delete_End()
{
if (head->Data() == -1122334455 && head->Next() == NULL)
{
cout << "List has no member so cannot delete end" << endl;
return;
}
// check if one in length
else if (head->Data() != -1122334455 && head->Next() == NULL)
{
head->SetData(-1122334455);
return;
}
// 2 or greater in length
else
{
Node* current;
Node* prev;
prev = head;
for (current = head->Next(); current->Next() != NULL; current = current->Next())
{
prev = current;
}
prev->SetNext(NULL);
return;
}
}
Node* List::Find(int data)
{
Node* current;
for (current = head; current != NULL && current->Data() != data; current = current->Next())
{}
if (current == NULL)
{
cout << "Did not find " << data << "." << endl;
return NULL;
}
else // found
{
cout << "Found " << data << "." << endl;
return current;
}
}
void List::Print()
{
if (head->Data() == -1122334455 && head->Next() == NULL)
{
cout << "List is empty." << endl;
return;
}
Node* current;
for (current = head; current != NULL; current = current->Next())
{
cout << current->Data() << " ";
}
cout << endl;
return;
}
Node.cpp
#include <cstddef>
#include "Node.h"
Node::Node()
{
// -1122334455 is an arbitrary integer that is likely to never be needed by the user
this->SetData(-1122334455);
this->SetNext(NULL);
return;
}
void Node::SetData(int aData)
{
this->data = aData;
return;
}
void Node::SetNext(Node* aNext)
{
this->next = aNext;
return;
}
int Node::Data()
{
return this->data;
}
Node* Node::Next()
{
return this->next;
}
I just implemented a Destructor and I am getting an “Access violation reading location”. I beleive the problem is in my while loop but just can't figure it out.
Below is my code. If needing to reference any other part of my List Class please let me know.
Thanks!
List::List():first(NULL), last(NULL), nodeListTotal(0)
{
}
List::~List()
{
Node* currentNode = first;
while( currentNode != 0 )
{
Node* temp = currentNode->getNext();
delete currentNode;
currentNode = temp;
}
first = 0;
}
Here is my entire List class. I have made the changes recommended, removed the first = 0; and change 0 to nullptr
#include "Node.h"
#include <string>
using namespace std;
class List
{
private:
int nodeListTotal;
Node* first;
Node* last;
public:
//Constructor
List();
//Destructor
~List();
//Copy-Constructor
//List(const List& theList);
//////Overloading Assignment Operator
//List& operator=(const List& L);
void push_back(Node*);
void push_front(Node*);
Node* pop_back();
Node* pop_front();
Node* getFirst() const;
Node* getLast() const;
int getListLength() const;
};
List::List():first(NULL), last(NULL), nodeListTotal(0)
{
}
// Destructor
List::~List()
{
Node* currentNode = first;
while( currentNode != nullptr )
{
Node* temp = currentNode->getNext();
delete currentNode;
currentNode = temp;
}
}
// Copy-Constructor
//List::List(const List& theList)
//{
// Node * tempPtr = new Node;
// tempPtr = theList.first;
// List(tempPtr);
//
// while (tempPtr != NULL)
// {
// Node * copyNode = new Node;
// copyNode = tempPtr;
// tempPtr = tempPtr->getNext();
// nodeListTotal++;
// }
//}
// Overloading Assignemnt Operator
//List& List::operator=(const List& L)
//{
// List* overList;
// Node* temp = L.first;
//
// while( temp != NULL ) {
// overList->getLast();
// temp = temp -> getNext();
//
// return *this;
//}
void List::push_back(Node* newNode)
{
Node* temp = last;
if (temp)
temp->setNext(newNode);
else
first = newNode;
last = newNode;
nodeListTotal++;
}
void List::push_front(Node* newNode)
{
Node* temp = getFirst();
newNode->setNext(temp);
first = newNode;
nodeListTotal++;
if (!temp)
last = first;
}
Node* List::pop_back()
{
Node* old = last;
if (first == last)
{
first = 0;
last = 0;
}
else
{
Node* temp = first;
for (int i = 0; i < (nodeListTotal - 1); i++)
{
temp = temp->getNext();
}
temp->setNext(NULL);
last = temp;
}
nodeListTotal--;
return old;
}
Node* List::pop_front()
{
Node* temp = getFirst();
first = temp->getNext();
if (!first)
last = 0;
nodeListTotal--;
return temp;
}
Node* List::getFirst() const
{
return first;
}
Node* List::getLast() const
{
return last;
}
int List::getListLength() const
{
return nodeListTotal;
}
Node.h
#include <string>
using namespace std;
class Node
{
private:
string dataItem;
string dataUnit;
int unitTotal;
Node* next;
public:
//Constructor
Node();
Node(int, string, string);
string getDescription( )const;
void setDescription(string);
string getQuantityName()const;
void setQuantityName(string);
int getQuantityNumber()const;
void setQuantityNumber(int);
Node* getNext( )const;
void setNext(Node*);
};
Node::Node(void):dataItem("None"), dataUnit("None"), unitTotal(0), next(NULL)
{
}
Node::Node(int q, string i, string u):dataItem(i), dataUnit(u), unitTotal(q), next(NULL)
{
}
string Node::getDescription( ) const
{
return dataItem;
}
void Node::setDescription(string iSetter)
{
dataItem = iSetter;
}
string Node::getQuantityName() const
{
return dataUnit;
}
void Node::setQuantityName(string uSetter)
{
dataUnit = uSetter;
}
int Node::getQuantityNumber() const
{
return unitTotal;
}
void Node::setQuantityNumber(int tSetter)
{
unitTotal = tSetter;
}
Node* Node::getNext() const
{
return next;
}
void Node::setNext(Node* nSetter)
{
next = nSetter;
}
Driver.cpp
int main( )
{
//===============================================
// PART ONE
//===============================================
cout << "\nPart I: push_front and pop_front\n";
cout << "\n----------------------------------\n";
List groceries;
// test push_back function
groceries.push_front(new Node(1, "gallon", "milk") );
groceries.push_front(new Node(2, "loaves", "bread") );
groceries.push_front(new Node(1, "dozen", "eggs" ) );
groceries.push_front(new Node(1, "package", "bacon") );
cout << "\nThe original nodes in the List:\n";
printList(groceries);
cout << "\n----------------------------------\n";
// test push_front function
cout << "\nAdding to the front of the List:\n";
cout << "\n----------------------------------\n";
groceries.push_front(new Node(2, "lbs", "hamburger") );
groceries.push_front(new Node(1, "dozen", "hamburger buns") );
printList(groceries);
cout << "\n----------------------------------\n";
// test pop-front
cout << "\nRemoving the first node from the list.\n";
cout << "\n----------------------------------\n";
Node* item = groceries.pop_front( );
cout << "\nPopped " << item->getDescription( ) << " from the list.\n\n";
printList(groceries);
if (item != NULL)
delete item;
// ===============================================
// PART TWO: Uncomment this block to test part two
// ===============================================
cout << "\n----------------------------------\n";
cout << "\nPart Two: Push_back and pop_back";
// test push_back
groceries.push_back(new Node(2, "cans", "orange juice") );
groceries.push_back(new Node(1, "lb", "swiss cheese") );
cout << "\nAdding two nodes at the end\n";
cout << "\n----------------------------------\n";
printList(groceries);
// test pop-back
cout << "\n----------------------------------\n";
cout << "\nRemove last node from the list\n";
cout << "\n----------------------------------\n";
item = groceries.pop_back( );
cout << "\nPopped " << item->getDescription( ) << " from the list.\n\n";
printList(groceries);
if (item != NULL)
delete item;
// ============================================
// end of part two
// ============================================
// ================================================
// PART THREE: uncomment this block to test part three
// ================================================
/*
// create a second list to test assignment
cout << "\n\n--------------extra credit------------------\n";
cout << "\n\n overloaded assignment operator\n";
cout << "The hardware list ...\n";
cout << "\n-------------------------------------------\n";
List hardware;
hardware.push_back(new Node(2, "lbs", "nails") );
hardware.push_back( new Node(3, "gals", "white paint") );
hardware.push_back(new Node(1, "piece", "plywood") );
printList(hardware);
hardware = groceries;
cout << "\n-------------------------------------------\n";
cout << "\nafter assignment";
cout << "\n-------------------------------------------\n";
printList(hardware);
cout << "\n-------------------------------------------\n";
cout << "\nTest the copy constructor\n";
cout << "\n-------------------------------------------\n";
printFirstNode(hardware);
// ==============================================
// end of part 3
// ==============================================
*/
cout << "\n-------------------------------------------\n";
cout << "\nEnd of Test";
cout << "\n-------------------------------------------\n";
system("PAUSE");
return 0;
}
Looks like pop back does not remove last node from the list, but returns it. Then the node is deleted and in list's destructor you try to delete it second time.
The push_back() method could be your culprit, coupled with the failure to initialize Node:next to zero. If only one node is added to the list using push_back then the value of that node's next member would be unknown and in the destructor an attempt to delete a second node referring to a random memory location would cause the access error.
Either ensure that the nodes values are initialized, or ensure that node.next is set explicitly in push_back() if it's the first node added to the list.
Something to note on pop_back() and pop_front().
Calling pop_back() on an empty list would still decrement nodeListTotal.
Calling pop_front() on an empty list would actually cause an access violation trying to access address 0.
The below deletion of node is causing the problem while cleaning up in the destructor.
if (item != NULL)
delete item;
When you do the pop_back you're deleting that(last) node in main(), but what happens to the node which is before that? it was pointing to the one which you deleted and is not set to NULL.
So, in the destructor when you start deleting the nodes you're checking for the NULL value of the nodes. All goes fine but now for the last one next wasn't set to NULL, instead it is still pointing to the one which you just deleted. Hence, tries to free the node which is already freed.
Then your code crashes.
Set the previous node's next to NULL whenever you're freeing the successive node.