Valgrind error invalid read of size 4 - c++

void ChoreStack::add(int new_urgency_level, string new_job){
for(int i = 0; i < length_; i++){
Chore* temp_chore = chore_array_[i];
if(temp_chore->level == new_urgency_level){
temp_chore->joblist.append(new_job+"^");
}
chore_array_[i] = temp_chore;
free(temp_chore);
}
}
Hello, this is the part that Valgrind says "Invalid read of size 4". I have never learned anything related to memory allocation before. Could anyone please explain to me in details why there is a memory error and how to fix it? Thank you!
Here is my declaration:
class ChoreStack{
public:
ChoreStack();
~ChoreStack();
void initialize(int new_urgency_level);
void add(int new_urgency_level, string new_chore);
void erase(int erase_level);
void print();
void next();
int get_length();
private:
struct Chore{
int level;
string joblist;
};
Chore** chore_array_;
int length_;
string* split_joblist(string joblist);
int number_of_chores(string joblist);
};
ChoreStack::ChoreStack(): chore_array_(nullptr), length_(0){
}
ChoreStack::~ChoreStack(){
delete[] chore_array_;
}
void ChoreStack::initialize(int new_urgency_level){
delete[] chore_array_;
chore_array_ = new Chore*[new_urgency_level];
for(int i = 0; i < new_urgency_level; i++){
Chore* temp_chore = new Chore;
temp_chore->level = i+1;
temp_chore->joblist = "";
chore_array_[new_urgency_level-i-1] = temp_chore;
delete temp_chore;
}
length_ = new_urgency_level;
cout << "New priority list with levels 1-" << length_ << " initialized." << endl;
}
Here is the part related to ChoreStack::add() function in main():
int main(){
ChoreStack c;
string cmd_line;
string* cmd_ptr = new string[3];
bool initialized = false;
while(true){
cout << "chores> ";
getline(cin, cmd_line);
int cmd_num = 0;
if(cmd_line.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 ") != string::npos){
cout << "Error: invalid input." << endl;
} else {
int begin_i = 0, space_occurence = 0;
unsigned int i = 0;
while(i <= cmd_line.length()){
if(cmd_line[i] == ' ' || i == cmd_line.length()){
space_occurence++;
cmd_num++;
if(space_occurence == 3){
cmd_ptr[cmd_num-1] = cmd_line.substr(begin_i);
break;
} else {
cmd_ptr[cmd_num-1] = cmd_line.substr(begin_i, i - begin_i);
}
begin_i = i + 1;
}
i++;
}
string command = cmd_ptr[0];
if(command == "init"){
if(cmd_num == 1){
cout << "Error: the number of priority levels is missing." << endl;
} else if(cmd_num > 2){
cout << "Error: too much input for initializing." << endl;
} else {
if(cmd_ptr[1].find_first_not_of("1234567890") != string::npos){
cout << "Error: the number of priority levels must be an integer larger than 0." << endl;
} else if(stoi(cmd_ptr[1]) < 1){
cout << "Error: it is not possible to create a priority list with 0 or less levels." << endl;
} else {
c.initialize(stoi(cmd_ptr[1]));
initialized = true;
}
}
} else if(command == "add"){
if(!initialized){
cout << "Error: no priority list is initialized." << endl;
} else {
if(cmd_num == 1){
cout << "Error: priority level and chore description are missing." << endl;
} else if(cmd_ptr[1].find_first_not_of("1234567890") != string::npos
|| stoi(cmd_ptr[1]) < 1
|| stoi(cmd_ptr[1]) > c.get_length()){
cout << "Error: priority level must be an integer between 1-3." << endl;
} else if(cmd_num == 2){
cout << "Error: chore description is missing." << endl;
} else {
c.add(stoi(cmd_ptr[1]), cmd_ptr[2]);
}
}
}
Here is the error message:
invalid read of size 4:
1.ChoreStack::add(int,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>)
2.main
Address 0x5a9de70 is 0 bytes inside a block of size 40 free'd
1. operator delete(void*)
2. ChoreStack::initialize(int)
3. main
Block was alloc'd at
1. operator new(unsigned long)
2. ChoreStack::initialize(int)
3. main
and there are lot more errors in the same form of this one...

This is a classic access after free.
void ChoreStack::initialize(int new_urgency_level){
delete[] chore_array_;
chore_array_ = new Chore*[new_urgency_level];
for(int i = 0; i < new_urgency_level; i++){
Chore* temp_chore = new Chore;
temp_chore->level = i+1;
temp_chore->joblist = "";
chore_array_[new_urgency_level-i-1] = temp_chore;
delete temp_chore;
}
Look closely at this code. You create a pointer called temp_chore. It points to an object you allocate with new. You then copy the value of temp_chore into chore_array_. So now the array has a pointer to the object you allocated with new.
But then you delete the object you allocated. So now, chore_array_ has a pointer to an object you deleted. It is an error to attempt to dereference this pointer since the thing it points to no longer exists.
But then in add, we have:
Chore* temp_chore = chore_array_[i];
if(temp_chore->level == new_urgency_level){
So temp_chore->level is an attempt to access the level member of the object temp_chore points to. But you deleted that object.
// Once you do this,
chore_array_[new_urgency_level-i-1] = temp_chore;
// this
delete temp_chore;
// does the same thing as this
delete chore_array_[new_urgency_level-i-1];
// because they're equal
You will find things much easier if you keep collections of values rather than collections of raw pointers.

Related

While performing the delete operation in the following code, I am not able to get the correct values returned in the following code.

Unable to print the correct values of deleted elements from the queue. DeQueue() is used for deleting the elements. I have used dynamic circular array implementation technique for the QUEUE, i.e., doubling of the memory when the QUEUE is full. The first element returned in the deletion process is correct but after that, if deletion is performed again, then random values are returned.
#include <iostream>
#include <cstdlib>
#include <math.h>
using namespace std;
struct DynArrayQueue{
int front, rear;
int capacity;
int* array;
};
void ResizeQueue(struct DynArrayQueue* Q);
struct DynArrayQueue* CreateDynQueue(){
struct DynArrayQueue* Q = (struct DynArrayQueue* )malloc(sizeof(struct DynArrayQueue));
if(!Q)
{
cout << "Memory Error in allocation!\n";
return NULL;
}
Q->capacity = 1;
Q->front = Q->rear = -1;
Q->array = (int* )malloc(Q->capacity * sizeof(int));
if(!Q->array)
{
cout << "Memory error in creating queue!\n";
return NULL;
}
return Q;
}
int IsEmptyQueue(struct DynArrayQueue* Q){
if(Q->front == -1){
cout << "Queue is empty!\n";
return 1;
}
else{
cout << "Queue not empty\n";
return 0;
}
}
int IsFullQueue(struct DynArrayQueue* Q){
if((((Q->rear)+1)%Q->capacity) == Q->front)
{
cout << "Queue is full!\n";
return 1;
}else{
cout << "Queue not full!\n";
return 0;
}
}
void QueueSize(struct DynArrayQueue* Q){
int s;
s = (Q->capacity - Q->front + Q->rear + 1) % Q->capacity;
cout << "Size of the queue is " << s;
cout << "\n\n";
}
void EnQueue(struct DynArrayQueue* Q){
int data;
cout << "Enter the data to be inserted:\n";
cin >> data;
if(IsFullQueue(Q))
{
ResizeQueue(Q);
}
Q->rear = (Q->rear + 1)%Q->capacity;
Q->array[Q->rear] = data;
if(Q->front == -1)
{
Q->front = Q->rear;
}
}
void ResizeQueue(struct DynArrayQueue* Q){
int size = Q->capacity;
Q->capacity = Q->capacity * 2;
Q->array = (int* )realloc(Q->array, Q->capacity);
if(!Q->array)
cout << "Memory error!\n";
if(Q->front > Q->rear)
{
for(int i = 0; i < Q->front; i++)
{
Q->array[i+size] = Q->array[i];
}
Q->rear = Q->rear + size;
}
}
void DeQueue(struct DynArrayQueue* Q){ **this function does not work properly**
int data = 0;
if(IsEmptyQueue(Q))
{
cout << "Queue Underflow!\n";
cout << "No element to delete!\n";
}
else{
data = Q->array[Q->front];
if(Q->front == Q->rear)
{
Q->front = Q->rear = -1;
}
else
{
Q->front = ((Q->front) + 1) % Q->capacity;
}
}
cout << "Element deleted is " << data;
cout << "\n";
}
void DeleteQueue(struct DynArrayQueue* Q){
if(Q){
if(Q->array)
{
free(Q->array);
}
free(Q);
}
}
int main()
{
int choice;
struct DynArrayQueue* Q1;
while(1)
{
cout << "1. Press to create a Queue:\n";
cout << "2. Enter an element in the queue:\n";
cout << "3. Delete an element from the queue:\n";
cout << "4. Press to know the Queue size:\n";
cout << "5. Press to know if Queue is full:\n";
cout << "6. Press to know if Queue is empty:\n";
cout << "7. Press enter to exit:\n";
cout << "Enter your choice:\n";
cin >> choice;
switch(choice)
{
case 1:
Q1 = CreateDynQueue();
break;
case 2:
EnQueue(Q1);
break;
case 3:
DeQueue(Q1);
break;
case 4:
QueueSize(Q1);
break;
case 5:
IsFullQueue(Q1);
break;
case 6:
IsEmptyQueue(Q1);
break;
case 7:
exit(0);
break;
default:
cout << "Wrong choice entered!\n";
break;
}
}
DeleteQueue(Q1);
}
The problem isn't in DeleteQueue, it is in ResizeQueue. When you call realloc, you're passing in the new number of elements but not factoring in the size of the elements (like you are when you initially allocate space). It should be
Q->array = (int* )realloc(Q->array, Q->capacity * sizeof(int));
The problem is in ResizeQueue().
The statement
Q->array = (int* )realloc(Q->array, Q->capacity);
does not allocate the right size. Subsequent operations (until the next DeleteQueue()) then happily assume the array is longer than it is, and write past the end.
Also, if realloc() fails it returns NULL and does not release the old memory. The result of this assignment is then a memory leak (since the old value of Q->array is lost to your program).
What you actually need to do (at minimum) is
int *temp = (int* )realloc(Q->array, Q->capacity * sizeof(int));
if (!temp)
cout << "Memory error!\n";
else
Q->array = temp;
Even that is not sufficient, since subsequent code uses Q->array and assumes the resizing succeeded. If a failure has occurred, the result is undefined behaviour. I'll leave addressing that concern properly as a learning exercise.
Also, in C++, consider using standard containers (like std::vector<int>) which can resize themselves in a controlled manner - so you can avoid silliness like allocating the wrong amounts of memory.

Seg Fault at return statement in function

My program is supposed to convert a prompt from infix to postfix. So far, through a debugger and various other methods, I have located the exact point at which I segfault, but don't understand why.
Here's my code:
Here's itop.h:
using namespace std;
#include <cstdlib>
#include <iostream>
class sNode{
public:
char data;
sNode *next;
};
class stack{
public:
sNode *head;
void push (char);
sNode pop();
int rank(char);
stack()
{
cout << "Initiliazing stack." << endl;
}
};
This is my itop.cpp file:
#include "itop.h"
void stack::push (char a)
{
// cout << "Pushing " << a << endl;
sNode *sn;
sn = new sNode;
sn->data = a;
sn->next = head;
head = sn;
}
sNode stack::pop()
{
// cout << "Popping stack." << endl;
sNode *sn;
sn = head;
head = head->next;
return *sn;
}
int stack::rank(char x)
{
int num = 0;
// cout << "Checking rank." << endl;
if(x == '\0')
{
num = 1;
// cout << "Checking for null" << endl;
return num;
}
else if(x == '+' || x == '-')
{
num = 2;
// cout << "Checking if + or -" << endl;
return num;
// cout << "After return." << endl;
}
else if(x == '*' || x == '/')
{
num = 3;
// cout << "Checking for * or /" << endl;
return num;
}
else
cout << "Error! Input not valid!" << endl;
}
And here's main.cpp:
using namespace std;
#include <iostream>
#include <cstdlib>
#include <cstring>
#include "itop.h"
int main()
{
char *temp1; //Instantiating variables.
char *temp2;
temp1 = new char[20];
temp2 = new char [20];
stack s;
do //Checking commands.
{
cout << "infix_to_postfix> ";
cin >> temp1;
if(strcmp(temp1, "quit") == 0)
{
return 0;
}
if(strcmp(temp1, "convert") != 0)
{
cout << "Error! Invalid command." << endl;
}
cin >> temp2;
if(strcmp(temp1, "convert") == 0)
{
for(int i=0; i<sizeof(temp2); i++)
{
if(isdigit(temp2[i]))
{
cout << atoi(&temp2[i]);
}
else if(s.rank(temp2[i]) < s.rank(s.head->data))
{
sNode temp = s.pop();
cout << temp.data;
}
else
{
s.push(temp2[i]);
}
}
}
else
{
cout << "Error! Command not supported." << endl;
}
}while(strcmp(temp1, "quit") != 0);
return 0;
}
The function is called at
else if(s.rank(temp2[i]) < s.rank(s.head->data))
And the problem is in here:
else if(x == '+' || x == '-')
{
num = 2;
// cout << "Checking if + or -" << endl;
return num;
// cout << "After return." << endl;
}
Specifically right before return num, I get "Segmentation fault (core dumped)" error message. I have used gdb and all I know is that right after "Checking if + or -" I see "$1 = 2". I'm not quite sure what that means, but it is what I want to return.
Thank you for your help.
There are many mistakes in your code. Your stack implementation is wrong. push() for example only sets head over and over again. This results in your stack class being able to ever only hold one element. next is never set to anything, so it contains random garbage. Further down, you have this:
for(int i=0; i<sizeof(temp2); i++)
sizeof(temp2) does not give you the amount of characters of the string temp2 points to. It gives you the size of the pointer temp2 itself. Furthermore, you end up reading s.head from an empty stack, which will be a pointer to random garbage. At that point, all bets are off of course. You can't expect anything else than a crash and burn.
Fix 1:Write a proper constructor.
stack()
{
head=NULL;
cout << "Initiliazing stack." << endl;
}
Fix 2:Write an extra method to check if stack is empty.
int stack::empty()
{
if(head == NULL)
return true;
else
return false;
}
Fix 3:Check if stack empty before using the stack data.
else if(!s.empty() && s.rank(temp2[i]) < s.rank(s.head->data))
{
...
}
Fix 4: Fix the rest of the code logic.

Size of dynamic array does not match the value submitted

I'm learning dynamic memory and something isn't quite going right. I have a function that accepts a number as input and is supposed to make an array of that size.
class Doctor {
public:
Doctor();
void fillOut();
void listPatients();
void patientReset();
~Doctor();
private:
string name;
int numPatients;
string *patientList;
};
Doctor::Doctor() {
name = "";
numPatients = 0;
patientList = new string[numPatients];
}
(Most relevant code in the 3rd code block).
void Doctor::fillOut()
{
string buffer = "";
string buffer2 = "";
size_t found;
bool valid = false;
int numP = 0;
int tester = 0;
bool validNum = false;
while(!valid)
{
cout << "Enter doctor name: ";
getline(cin, buffer);
found = buffer.find_first_of("1234567890!##$%^&*()-=_+/<>?;':][");
if(string::npos == found)
{
name = buffer;
valid = true;
}
}
while (!validNum)
{
cout << "\nEnter number of patients: ";
buffer = "";
getline(cin, buffer);
buffer2 = buffer;
stringstream ss(buffer);
if(ss >> tester)
{
stringstream ss2(buffer2);
ss2 >> numP;
validNum = true;
}
else
{
cout << "Not a number. Please try again." << endl;
}
}
patientList = new string[numP];
cout << patientList->size() << endl;
for(int i = 0; i < (numP + 0); i++)
{
valid = false;
while(!valid)
{
cout << "\nEnter patient " << (i + 1) << ": ";
getline(cin,buffer);
found = buffer.find_first_of("1234567890!##$%^&*()-=_+,./<>?;':][");
if(string::npos == found)
{
*(patientList + i - 0) = buffer;
//patientList[i-1] = buffer;
valid = true;
}
else
{
valid = false;
}
}
}
}
I then try to display the contents of the list.
void Doctor::listPatients()
{
cout << "size: " << patientList->size() << endl;
cout << "\nDoctor: " << name << endl;
for(int i = 0; i < (patientList->size() - 1); i++)
{
cout << "Patient " << (i+1) << ": " << patientList[i] << endl;
}
cout << "end patients" << endl;
}
But for some reason the number I submit as the size isn't the size of the array. For example, in fillOut() I have the function output the size. It outputs the size as 0 every time. Then in listPatients(), I have something that prints the size again, to verify I'm doing it right. If I initially input 3, it has outputs 5 in this function.
I'm completely mystified.
The line patientList->size() is equivalent to patientList[0].size(), the length of the initial string in your patientList array. At the point where you have just allocated your array, the result is always zero; in other instances, it is the length of the first string.
A preferred way of making containers in C++ is std::vector or std::array. In your case using std::vector is more appropriate, because your code allocates the patientList dynamically.
Ok, I found your problem. You are declaring an array of strings. But a raw array is really just a pointer to the contents of the array. When you call ->size, it dereferences that pointer, which points to the first string in the array and tells you the size of that string.
If you want an array that actually knows its size, use std::array.
OK so you declare patientList as a string* but when you call size() on this you are actually calling it on the first item in the array you have created. The only reason your code compiled is because string has a size() function and as this is the type contained in your array you got away with it because patientList actually points to the item at the start of the array (a string). In general an array does not have a size() function.
One other problem you will have though is here in your loop
for(int i = 0; i < (patientList->size() - 1); i++) {
^
^
cout << "Patient " << (i+1) << ": " << patientList[i] << endl;
}
this will cause your loop to terminate one before the end of patientList. You should use
for(int i = 0; i < patientList->size(); i++) {
cout << "Patient " << (i+1) << ": " << patientList[i] << endl;
}
You would be better off using a std::vector here to put your strings in. Also be careful here
if(string::npos == found) {
*(patientList + i - 0) = buffer;
//patientList[i-1] = buffer;
valid = true;
}
else {
valid = false;
}
because you would only be assigning to your array on the occasions when your if statement is true - the indices in the array when it is false are left uninitialised. I would honestly start again from scratch and use a vector of strings.

C++ - pointer being freed was not allocated error

malloc: *** error for object 0x10ee008c0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
Or I get this when I try and print everything
Segmentation fault: 11
I'm doing some homework for an OOP class and I've been stuck for a good hour now. I'm getting this error once I've used keyboard input enough. I am not someone who gets frustrated at all, and here I am getting very frustrated with this. Here are the files:
This is the book class. I'm pretty sure this is very solid. But just for reference:
//--------------- BOOK.CPP ---------------
// The class definition for Book.
//
#include <iostream>
#include "book.h"
using namespace std;
Book::Book()
//
{
strcpy(title, " ");
strcpy(author, " ");
type = FICTION;
price = 0;
}
void Book::Set(const char* t, const char* a, Genre g, double p)
{
strcpy(title, t);
strcpy(author, a);
type = g;
price = p;
}
const char* Book::GetTitle() const
{
return title;
}
const char* Book::GetAuthor() const
{
return author;
}
double Book::GetPrice() const
{
return price;
}
Genre Book::GetGenre() const
{
return type;
}
void Book::Display() const
{
int i;
cout << GetTitle();
for (i = strlen(title) + 1; i < 32; i++)
cout << (' ');
cout << GetAuthor();
for (i = strlen(author) + 1; i < 22; i++)
cout << (' ');
switch (GetGenre())
{
case FICTION:
cout << "Fiction ";
break;
case MYSTERY:
cout << "Mystery ";
break;
case SCIFI:
cout << "SciFi ";
break;
case COMPUTER:
cout << "Computer ";
break;
}
cout << "$";
if (GetPrice() < 1000)
cout << " ";
if (GetPrice() < 100)
cout << " ";
if (GetPrice() < 10)
cout << " ";
/* printf("%.2f", GetPrice());*/
cout << '\n';
}
This is the store class that deals with the array and dynamic allocation. This was working well without input commands, but just using its functions it was working like a champ.
//--------------- STORE.CPP ---------------
// The class definition for Store.
//
#include <iostream>
#include <cstring> // for strcmp
#include "store.h"
using namespace std;
Store::Store()
{
maxSize = 5;
currentSize = 0;
bookList = new Book[maxSize];
}
Store::~Store()
// This destructor function for class Store
// deallocates the Store's list of Books
{
delete [] bookList;
}
void Store::Insert(const char* t, const char* a, Genre g, double p)
// Insert a new entry into the direrctory.
{
if (currentSize == maxSize)// If the directory is full, grow it.
Grow();
bookList[currentSize++].Set(t, a, g, p);
}
void Store::Sell(const char* t)
// Sell a book from the store.
{
char name[31];
strcpy(name, t);
int thisEntry = FindBook(name);// Locate the name in the directory.
if (thisEntry == -1)
cout << *name << " not found in directory";
else
{
cashRegister = cashRegister + bookList[thisEntry].GetPrice();
// Shift each succeding element "down" one position in the
// Entry array, thereby deleting the desired entry.
for (int j = thisEntry + 1; j < currentSize; j++)
bookList[j - 1] = bookList[j];
currentSize--;// Decrement the current number of entries.
cout << "Entry removed.\n";
if (currentSize < maxSize - 5)// If the directory is too big, shrink it.
Shrink();
}
}
void Store::Find(const char* x) const
// Display the Store's matches for a title or author.
{
// Prompt the user for a name to be looked up
char name[31];
strcpy(name, x);
int thisBook = FindBook(name);
if (thisBook != -1)
bookList[thisBook].Display();
int thisAuthor = FindAuthor(name, true);
if ((thisBook == -1) && (thisAuthor == -1))
cout << name << " not found in current directory\n";
}
void Store::DisplayGenre(const Genre g) const
{
double genrePrice = 0;
int genreCount = 0;
for (int i = 0; i < currentSize; i++)// Look at each entry.
{
if (bookList[i].GetGenre() == g)
{
bookList[i].Display();
genrePrice = genrePrice + bookList[i].GetPrice();
genreCount++;
}
}
cout << "Number of books in this genre: " << genreCount
<< " " << "Total: $";
if (genrePrice < 1000)
cout << " ";
if (genrePrice < 100)
cout << " ";
if (genrePrice < 10)
cout << " ";
printf("%.2f", genrePrice);
}
void Store::DisplayStore() const
{
if (currentSize >= 1)
{
cout << "**Title**\t\t"
<< "**Author**\t"
<< "**Genre**\t"
<< "**Price**\n\n";
for (int i = 0; i < currentSize; i++)
bookList[i].Display();
}
else
cout << "No books currently in inventory\n\n";
cout << "Total Books = " << currentSize
<< "\nMoney in the register = $";
if (cashRegister < 1000)
cout << " ";
if (cashRegister < 100)
cout << " ";
if (cashRegister < 10)
cout << " ";
printf("%.2f", cashRegister);
cout << '\n';
}
void Store::Sort(char type)
{
Book temp;
for(int i = 0; i <= currentSize; i++)
{
for (int j = i+1; j < currentSize; j++)
{
if (type == 'A')
{
if (strcmp(bookList[i].GetTitle(), bookList[j].GetTitle()) > 0)
{
temp = bookList[i];
bookList[i] = bookList[j];
bookList[j] = temp;
}
}
if (type == 'T')
{
if (strcmp(bookList[i].GetAuthor(), bookList[j].GetAuthor()) > 0)
{
temp = bookList[i];
bookList[i] = bookList[j];
bookList[j] = temp;
}
}
}
}
}
void Store::SetCashRegister(double x)
// Set value of cash register
{
cashRegister = x;
}
void Store::Grow()
// Double the size of the Store's bookList
// by creating a new, larger array of books
// and changing the store's pointer to refer to
// this new array.
{
maxSize = currentSize + 5;// Determine a new size.
cout << "** Array being resized to " << maxSize
<< " allocated slots" << '\n';
Book* newList = new Book[maxSize];// Allocate a new array.
for (int i = 0; i < currentSize; i++)// Copy each entry into
newList[i] = bookList[i];// the new array.
delete [] bookList;// Remove the old array
bookList = newList;// Point old name to new array.
}
void Store::Shrink()
// Divide the size of the Store's bookList in
// half by creating a new, smaller array of books
// and changing the store's pointer to refer to
// this new array.
{
maxSize = maxSize - 5;// Determine a new size.
cout << "** Array being resized to " << maxSize
<< " allocated slots" << '\n';
Book* newList = new Book[maxSize];// Allocate a new array.
for (int i = 0; i < currentSize; i++)// Copy each entry into
newList[i] = bookList[i];// the new array.
delete [] bookList;// Remove the old array
bookList = newList;// Point old name to new array.
}
int Store::FindBook(char* name) const
// Locate a name in the directory. Returns the
// position of the entry list as an integer if found.
// and returns -1 if the entry is not found in the directory.
{
for (int i = 0; i < currentSize; i++)// Look at each entry.
if (strcmp(bookList[i].GetTitle(), name) == 0)
return i;// If found, return position and exit.
return -1;// Return -1 if never found.
}
int Store::FindAuthor(char* name, const bool print) const
// Locate a name in the directory. Returns the
// position of the entry list as an integer if found.
// and returns -1 if the entry is not found in the directory.
{
int returnValue;
for (int i = 0; i < currentSize; i++)// Look at each entry.
if (strcmp(bookList[i].GetAuthor(), name) == 0)
{
if (print == true)
bookList[i].Display();
returnValue = i;// If found, return position and exit.
}
else
returnValue = -1;// Return -1 if never found.
return returnValue;
}
Now this is the guy who needs some work. There may be some stuff blank so ignore that. This one controls all the input, which is the problem I believe.
#include <iostream>
#include "store.h"
using namespace std;
void ShowMenu()
// Display the main program menu.
{
cout << "\n\t\t*** BOOKSTORE MENU ***";
cout << "\n\tA \tAdd a Book to Inventory";
cout << "\n\tF \tFind a book from Inventory";
cout << "\n\tS \tSell a book";
cout << "\n\tD \tDisplay the inventory list";
cout << "\n\tG \tGenre summary";
cout << "\n\tO \tSort inventory list";
cout << "\n\tM \tShow this Menu";
cout << "\n\tX \teXit Program";
}
char GetAChar(const char* promptString)
// Prompt the user and get a single character,
// discarding the Return character.
// Used in GetCommand.
{
char response;// the char to be returned
cout << promptString;// Prompt the user
cin >> response;// Get a char,
response = toupper(response);// and convert it to uppercase
cin.get();// Discard newline char from input.
return response;
}
char Legal(char c)
// Determine if a particular character, c, corresponds
// to a legal menu command. Returns 1 if legal, 0 if not.
// Used in GetCommand.
{
return((c == 'A') || (c == 'F') || (c == 'S') ||
(c == 'D') || (c == 'G') || (c == 'O') ||
(c == 'M') || (c == 'X'));
}
char GetCommand()
// Prompts the user for a menu command until a legal
// command character is entered. Return the command character.
// Calls GetAChar, Legal, ShowMenu.
{
char cmd = GetAChar("\n\n>");// Get a command character.
while (!Legal(cmd))// As long as it's not a legal command,
{// display menu and try again.
cout << "\nIllegal command, please try again . . .";
ShowMenu();
cmd = GetAChar("\n\n>");
}
return cmd;
}
void Add(Store s)
{
char aTitle[31];
char aAuthor[21];
Genre aGenre = FICTION;
double aPrice = 10.00;
cout << "Enter title: ";
cin.getline(aTitle, 30);
cout << "Enter author: ";
cin.getline(aAuthor, 20);
/*
cout << aTitle << " " << aAuthor << "\n";
cout << aGenre << " " << aPrice << '\n';
*/
s.Insert(aTitle, aAuthor, aGenre, aPrice);
}
void Find()
{
}
void Sell()
{
}
void ViewGenre(Store s)
{
char c;
Genre result;
do
c = GetAChar("Enter Genre - (F)iction, (M)ystery, (S)ci-Fi, or (C)omputer: ");
while ((c != 'F') && (c != 'M') && (c != 'S') && (c != 'C'));
switch (result)
{
case 'F': s.DisplayGenre(FICTION); break;
case 'M': s.DisplayGenre(MYSTERY); break;
case 'S': s.DisplayGenre(SCIFI); break;
case 'C': s.DisplayGenre(COMPUTER); break;
}
}
void Sort(Store s)
{
char c;
Genre result;
do
c = GetAChar("Enter Genre - (F)iction, (M)ystery, (S)ci-Fi, or (C)omputer: ");
while ((c != 'A') && (c != 'T'));
s.Sort(c);
}
void Intro(Store s)
{
double amount;
cout << "*** Welcome to Bookstore Inventory Manager ***\n"
<< "Please input the starting money in the cash register: ";
/* cin >> amount;
s.SetCashRegister(amount);*/
}
int main()
{
Store mainStore;// Create and initialize a Store.
Intro(mainStore);//Display intro & set Cash Regsiter
ShowMenu();// Display the menu.
/*mainStore.Insert("A Clockwork Orange", "Anthony Burgess", SCIFI, 30.25);
mainStore.Insert("X-Factor", "Anthony Burgess", SCIFI, 30.25);*/
char command;// menu command entered by user
do
{
command = GetCommand();// Retrieve a command.
switch (command)
{
case 'A': Add(mainStore); break;
case 'F': Find(); break;
case 'S': Sell(); break;
case 'D': mainStore.DisplayStore(); break;
case 'G': ViewGenre(mainStore); break;
case 'O': Sort(mainStore); break;
case 'M': ShowMenu(); break;
case 'X': break;
}
} while ((command != 'X'));
return 0;
}
Please, any and all help you can offer is amazing.
Thank you.
Welcome to the exciting world of C++!
Short answer: you're passing Store as a value. All your menu functions should take a Store& or Store* instead.
When you're passing Store as a value then an implicit copy is done (so the mainStore variable is never actually modified). When you return from the function the Store::~Store is called to clean up the copied data. This frees mainStore.bookList without changing the actual pointer value.
Further menu manipulation will corrupt memory and do many double frees.
HINT: If you're on linux you can run your program under valgrind and it will point out most simple memory errors.
Your Store contains dynamically-allocated data, but does not have an assignment operator. You have violated the Rule of Three.
I don't see the Store class being instantiated anywhere by a call to new Store() which means the booklist array has not been created but when the program exits and calls the destructor, it tries to remove the array that was never allocated and hence that's why i think you are getting this error. Either, modify the destructor to have a null check or instantiate the class by a call to the constructor. Your code shouldn't still be working anywhere you are trying to use a Store object.
Hope this helps

Reading input from a text file, omits the first and adds a nonsense value to the end?

When I input locations from a txt file I am getting a peculiar error where it seems to miss off the first entry, yet add a garbage entry to the end of the link list (it is designed to take the name, latitude and longitude for each location you will notice). I imagine this to be an issue with where it starts collecting the inputs and where it stops but I cant find the error!! It reads the first line correctly but then skips to the next before adding it because during testing for the bug it had no record of the first location Lisbon though whilst stepping into the method call it was reading it. Very bizarre but hopefully someone knows the issue. Here is firstly my header file:
#include <string>
struct locationNode
{
char nodeCityName [35];
double nodeLati;
double nodeLongi;
locationNode* Next;
void CorrectCase() // Correct upper and lower case letters of input
{
int MAX_SIZE = 35;
int firstLetVal = this->nodeCityName[0], letVal;
int n = 1; // variable for name index from second letter onwards
if((this->nodeCityName[0] >90) && (this->nodeCityName[0] < 123)) // First letter is lower case
{
firstLetVal = firstLetVal - 32; // Capitalise first letter
this->nodeCityName[0] = firstLetVal;
}
while(n <= MAX_SIZE - 1)
{
if((this->nodeCityName[n] >= 65) && (this->nodeCityName[n] <= 90))
{
letVal = this->nodeCityName[n] + 32;
this->nodeCityName[n] = letVal;
}
n++;
}
//cityNameInput = this->nodeCityName;
}
};
class Locations
{
private:
int size;
public:
Locations(){
}; // constructor for the class
locationNode* Head;
//int Add(locationNode* Item);
};
And here is the file containing main:
// U08221.cpp : main project file.
#include "stdafx.h"
#include "Locations.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int n = 0,x, locationCount = 0, MAX_SIZE = 35;
string cityNameInput;
char targetCity[35];
bool acceptedInput = false, userInputReq = true, match = false, nodeExists = false;// note: addLocation(), set to true to enable user input as opposed to txt file
locationNode *start_ptr = NULL; // pointer to first entry in the list
locationNode *temp, *temp2; // Part is a pointer to a new locationNode we can assign changing value followed by a call to Add
locationNode *seek, *bridge;
void setElementsNull(char cityParam[])
{
int y=0, count =0;
while(cityParam[y] != NULL)
{
y++;
}
while(y < MAX_SIZE)
{
cityParam[y] = NULL;
y++;
}
}
void addLocation()
{
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
if(!userInputReq) // bool that determines whether user input is required in adding the node to the list
{
cout << endl << "Enter the name of the location: ";
cin >> temp->nodeCityName;
temp->CorrectCase();
setElementsNull(temp->nodeCityName);
cout << endl << "Please enter the latitude value for this location: ";
cin >> temp->nodeLati;
cout << endl << "Please enter the longitude value for this location: ";
cin >> temp->nodeLongi;
cout << endl;
}
temp->Next = NULL; //set to NULL as when one is added it is currently the last in the list and so can not point to the next
if(start_ptr == NULL){ // if list is currently empty, start_ptr will point to this node
start_ptr = temp;
}
else
{ temp2 = start_ptr;
// We know this is not NULL - list not empty!
while (temp2->Next != NULL)
{
temp2 = temp2->Next; // Move to next link in chain until reach end of list
}
temp2->Next = temp;
}
++locationCount; // increment counter for number of records in list
if(!userInputReq){
cout << "Location sucessfully added to the database! There are " << locationCount << " location(s) stored" << endl;
}
}
void populateList(){
ifstream inputFile;
inputFile.open ("locations.txt", ios::in);
userInputReq = true;
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
do
{
inputFile.get(temp->nodeCityName, 35, ' ');
setElementsNull(temp->nodeCityName);
inputFile >> temp->nodeLati;
inputFile >> temp->nodeLongi;
setElementsNull(temp->nodeCityName);
if(temp->nodeCityName[0] == 10) //remove linefeed from input
{
for(int i = 0; temp->nodeCityName[i] != NULL; i++)
{
temp->nodeCityName[i] = temp->nodeCityName[i + 1];
}
}
addLocation();
}
while(!inputFile.eof());
userInputReq = false;
cout << "Successful!" << endl << "List contains: " << locationCount << " entries" << endl;
cout << endl;
inputFile.close();
}
bool nodeExistTest(char targetCity[]) // see if entry is present in the database
{
match = false;
seek = start_ptr;
int letters = 0, letters2 = 0, x = 0, y = 0;
while(targetCity[y] != NULL)
{
letters2++;
y++;
}
while(x <= locationCount) // locationCount is number of entries currently in list
{
y=0, letters = 0;
while(seek->nodeCityName[y] != NULL) // count letters in the current name
{
letters++;
y++;
}
if(letters == letters2) // same amount of letters in the name
{
y = 0;
while(y <= letters) // compare each letter against one another
{
if(targetCity[y] == seek->nodeCityName[y])
{
match = true;
y++;
}
else
{
match = false;
y = letters + 1; // no match, terminate comparison
}
}
}
if(match)
{
x = locationCount + 1; //found match so terminate loop
}
else{
if(seek->Next != NULL)
{
bridge = seek;
seek = seek->Next;
x++;
}
else
{
x = locationCount + 1; // end of list so terminate loop
}
}
}
return match;
}
void deleteRecord() // complete this
{
int junction = 0;
locationNode *place;
cout << "Enter the name of the city you wish to remove" << endl;
cin >> targetCity;
setElementsNull(targetCity);
if(nodeExistTest(targetCity)) //if this node does exist
{
if(seek == start_ptr) // if it is the first in the list
{
junction = 1;
}
if(seek != start_ptr && seek->Next == NULL) // if it is last in the list
{
junction = 2;
}
switch(junction) // will alter list accordingly dependant on where the searched for link is
{
case 1:
start_ptr = start_ptr->Next;
delete seek;
--locationCount;
break;
case 2:
place = seek;
seek = bridge;
delete place;
--locationCount;
break;
default:
bridge->Next = seek->Next;
delete seek;
--locationCount;
break;
}
}
else
{ cout << targetCity << "That entry does not currently exist" << endl << endl << endl;
}
}
void searchDatabase()
{
char choice;
cout << "Enter search term..." << endl;
cin >> targetCity;
if(nodeExistTest(targetCity))
{
cout << "Entry: " << endl << endl;
}
else
{
cout << "Sorry, that city is not currently present in the list." << endl << "Would you like to add this city now Y/N?" << endl;
cin >> choice;
/*while(choice != ('Y' || 'N'))
{
cout << "Please enter a valid choice..." << endl;
cin >> choice;
}*/
switch(choice)
{
case 'Y':
addLocation();
break;
case 'N':
break;
default :
cout << "Invalid choice" << endl;
break;
}
}
}
void printDatabase()
{
temp = start_ptr; // set temp to the start of the list
do
{ if (temp == NULL)
{
cout << "You have reached the end of the database" << endl;
}
else
{ // Display details for what temp points to at that stage
cout << "Location : " << temp->nodeCityName << endl;
cout << "Latitude : " << temp->nodeLati << endl;
cout << "Longitude : " << temp->nodeLongi << endl;
cout << endl;
// Move on to next locationNode if one exists
temp = temp->Next;
}
}
while (temp != NULL);
}
void nameValidation(string name)
{
n = 0; // start from first letter
x = name.size();
while(!acceptedInput)
{
if((name[n] >= 65) && (name[n] <= 122)) // is in the range of letters
{
while(n <= x - 1)
{
while((name[n] >=91) && (name[n] <=97)) // ERROR!!
{
cout << "Please enter a valid city name" << endl;
cin >> name;
}
n++;
}
}
else {
cout << "Please enter a valid city name" << endl;
cin >> name;
}
if(n <= x - 1)
{
acceptedInput = true;
}
}
cityNameInput = name;
}
int main(array<System::String ^> ^args)
{
//main contains test calls to functions at present
cout << "Populating list...";
populateList();
printDatabase();
deleteRecord();
printDatabase();
cin >> cityNameInput;
}
The text file contains this (ignore the names, they are just for testing!!):
Lisbon 45 47
Fattah 45 47
Darius 42 49
Peter 45 27
Sarah 85 97
Michelle 45 47
John 25 67
Colin 35 87
Shiron 40 57
George 34 45
Sean 22 33
The output omits Lisbon, but adds on a garbage entry with nonsense values. Any ideas why? Thank you in advance.
The main function creates a new locationNode and stores it in the global variable temp, then reads the first dataset and stores it in that node.
Then you call addLocation() which starts by creating another new locationNode which replaces the existing one in temp. This new node is then inserted into the list.
The next iteration of the main loop then fills that temp value with values and addLocation() inserts another brand new locationNode into the list. So the first dataset isn't stored in the list and each iteration ends up by inserting an uninitialized new node.
As you see using global variables can lead to confusing situations and you code would surely become clearer if you were passing the nodes as parameters to your functions.