So I'm writing a program that creates a library for a collection of CDs and displays them. My program compiles but crashes whenever I write an array of pointers to songs from a file into structs contained within an array shown here:
//Get song array
for (int a = 0; a < num_songs; a++)
{
getline (infile, line);
sub = line.c_str();
word = createString(sub);
length = substr(word, -1, 5);
title = substr(word, 5, strlen(sub));
cd->song_array[a] = createSong(title,length);
destroyString(word);
}
I think it's due to undefined behavior, here's the .cpp file that this is happening in.
#include <iostream>
#include "CDs.h"
#include "CD.h"
#include <fstream>
#include <string>
#include <cstring>
using namespace std;
//Creates a collection of CDs
CDs* createCDs(const char* file_name)
{
//Declare variables and allocate memory
int max_cds = 50;
CDs* collection = new CDs;
collection->max_cds = max_cds;
CD** cd_array = new CD*[max_cds];
int num;
int sentinel = 0;
String* word;
string line;
CD* cd;
const char* sub;
String* length;
String* title;
//Open .txt file
ifstream infile;
infile.open(file_name);
if (infile.is_open())
{
while (infile.good())
{
for (int i = 0; i < max_cds; i++)
{
//Get the artist from .txt file
cd = cd_array[i];
getline (infile, line);
sub = line.c_str();
word = createString(sub); //Create string from infile line
cd->artist = word;
destroyString(word);
//Get the Title of the album from file
getline (infile, line);
sub = line.c_str();
word = createString(sub);
cd->title = word;
destroyString(word);
//Get the Year of the album from file
infile >> num;
cd->year = num;
//Get the Rating
infile >> num;
cd->rating = num;
//Get number of tracks
int num_songs;
infile >> cd->num_tracks;
//Get song array
for (int a = 0; a < num_songs; a++)
{
getline (infile, line);
sub = line.c_str();
word = createString(sub);
cout << "SHIT" << endl;
length = substr(word, -1, 5);
title = substr(word, 5, strlen(sub));
cd->song_array[a] = createSong(title,length);
destroyString(word);
}
cd_array[i] = cd;
sentinel++;
}
}
}
else
{
cout << "file did not open";
}
collection->cd_array = cd_array;
collection->num_cds = sentinel;
collection->max_cds = max_cds;
return collection;
}
I have no idea what to do to make this run, If someone could help that would be amazing.
edit - I didn't give the .cpp that is included and has some of the functions used
#include <iostream>
#include <cstring>
#include "String.h"
using namespace std;
//Function that creates a string
String* createString(const char* char_array)
{
//Allocate memory for a pointer to String struct
//String* string;
String* string = new String;
//Write the char_array to String struct
int length = strlen(char_array);
char array[30];
for (int i = 0; i <= length; i++)
{
array[i] = char_array[i];
string->array[i] = array[i];
}
return string;
}
//Function that displays the string
void displayString(String* str)
{
for (int i = 0; i < strlen(str->array); i++)
{
cout << str->array[i];
}
cout << endl;
}
//Function that destroys the string
void destroyString(String* str)
{
delete str;
str = NULL;
}
int find(String* str, char delimiter, int start)
{
for (int i = start; i <= strlen(str->array); i++)
{
if (str->array[i] == delimiter)
{
return i;
}
}
cout << "No occurences of delimiter were found" << endl;
return -1;
}
String* substr(String* str, int start, int end)
{
String* new_str = new String;
int count = 0;
for (int i = start + 1; i < end - 1; i++)
{
new_str->array[count] = str->array[i];
count++;
}
return new_str;
}
void compare(String* str1, String* str2)
{
if (str1->array < str2->array)
{
cout << str1->array << " is less than " << str2->array << endl;
}
if (str1 > str2)
{
cout << str2->array <<" is less than " << str1->array << endl;
}
if (str1 == str2)
{
cout << "The strings are equal" << endl;
}
}
You never allocate memory for the effective CD. You just allocate an array of pointers to CDs (cd_array). This means you have an array of pointers, pointing to unkown memory locations.
The best way to ensure no such bad access is possible, is to not dynamically allocate memory. Just use CD cd_array[max_cds] and work with that. (Use call by reference, if you need to pass this to a function.)
You need to add the following lines:
//Get the artist from .txt file
cd_array[i] = new CD;
cd = cd_array[i];
.....
You should de-allocate the memory once finished using them
for(...)
delete cd_array[i];
delete []cd_array;
Related
Having trouble compiling program. Should read input from text file and store into a dynamic array of structure. I'm prettty sure my problem lies in the ipod_Initialize function. I'm not sure how to pass the dynamic array structure through the function.
#include <iostream>
#include <string>
#include <fstream>
# include <ctime>
using namespace std;
struct Song
{
string title;
string artist;
float size{};
};
int song_Count(string);
void ipod_Initialize(string, int, Song);
void show_playlist();
int main(char *argv[]) {
// argv is filename
int count = song_Count(argv[1]);
Song* songInfo = new Song[count];
ipod_Initialize(argv[1], count, *songInfo);
cout << songInfo[0].title;
delete[] songInfo;
return 0;
}
int song_Count(string fileName){
ifstream inFile;
string data;
int count = 0;
inFile.open(fileName);
if (!inFile.is_open()){
cout << "Error opening file" << endl;
return 0;
}
while(!inFile.eof()){
getline(inFile, data);
if (!data.empty()){
count++;
}
}
inFile.close();
count = count / 3 + 1;
return count;
}
void ipod_Initialize(string fileName, int count, Song* songInfo) {
string data;
ifstream inFile;
inFile.open(fileName);
for (int i = 0; i <= count; i++) {
getline(inFile, data);
if (!data.empty())
songInfo[i].title = data;
getline(inFile, data);
if (!data.empty())
songInfo[i].artist = data;
//getline(inFile, mem);
if (!data.empty())
inFile >> songInfo[i].size;
}
}
So I've overloaded an operator ">>" for my class MyString. It was supposed to read information from text file to a custom-made string, however I'm getting an exception when I'm trying to fill string char by char. Text.txt contains simply "xyz"
Main.cpp:
#include "MyString.h"
#include <fstream>
#include <iostream>
//TO-DO 1 CharArr for all
int main() {
MyString NewString;
ifstream In("Text.txt");
In >> NewString;
cout << NewString << endl;
system("pause");
return 0;
}
Constructors:
MyString::MyString() {
StringLength = 0;
Pointer = nullptr;
}
MyString::MyString(const char* String) {
for (int i = 0; String[i]; i++)
StringLength++;
Pointer = new char[StringLength + 1];
char *Source = (char *)String;
char *Destination = (char *)Pointer;
for (int i = 0; i < StringLength + 1; i++)
Destination[i] = Source[i];
}
Operator:
istream &operator>>(istream &In, MyString &String) {
int FileStringLength = 0;
char Character;
if (String.Pointer != nullptr)
delete[] String.Pointer;
while (In.get(Character) && Character != '\n')
FileStringLength++;
if (FileStringLength < 1000 && FileStringLength != 0) {
String.StringLength = FileStringLength;
In.clear(), In.seekg(0, ios::beg);
for (int i = 0; In.get(Character) && Character != '\n'; i++)
String.Pointer[i] = Character; // I get an exception here
}
else if (!FileStringLength) {
cout << "File is empty." << endl;
}
else {
cout << "File contains too many characters." << endl;
}
return In;
};
if (String.Pointer != nullptr)
delete[] String.Pointer;
freed the storage that
String.Pointer[i] = Character; // I get an exception here
was going to use.
Solution:
Don't delete[] the storage. Instead write over it until the storage is full, then allocate a new, larger buffer for storage, copy the old buffer into the new buffer, and then free the old buffer.
I keep getting an error of bad memory allocation. I've spent the whole night trying to find where I went wrong but I can't figure out what.
I've combed through every line but still nothing. Could it be that my program/laptop just isn't strong enough?
Any help would be extremely helpful. My head is ringing and I need some rest.
Here's my code:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
// struct to store word + count combinations
struct wordItem{
string word;
int count;
};
void getStopWords(char *ignoreWordFileName, vector<string>& _vecIgnoreWords);
bool isCommonWord(string word, vector<string>& _vecIgnoreWords);
void printTopN(wordItem wordItemList[], int topN);
void doubleArray(wordItem wordItemList[], int size);
int getTotalNumberNonCommonWords(wordItem wordItemList[], int size, int wordCount);
const int STOPWORD_LIST_SIZE = 50;
// ./a.out 10 HW1-HungerGames_edit.txt HW1-ignoreWords.txt
int main(int argc, char* argv[]){
vector<string> vecIgnoreWords(STOPWORD_LIST_SIZE);
// verify we have the correct # of parameters, else throw error msg & return
if (argc != 4){
cout << "Usage: ";
cout << argv[0] << " <number of words> <filename.txt> <ignorefilename.txt>"<< endl;
return 0;
}
//Set vector with stop words
getStopWords(argv[3], vecIgnoreWords);
//initialize struct array
int aSize = 100;
wordItem *theStructArray = new wordItem[aSize];
int counter = 0;
int doubleCount = 0;
//read main txt file
ifstream inFile(argv[1]);
if(inFile.is_open()){
string line;
string theWord;
//extract words from file
while(getline(inFile, line)){
istringstream iss(line);
//extract and analyze word
while(iss >> theWord){
if(!(isCommonWord(theWord, vecIgnoreWords))){
bool inStructArray = false;
int inStructPosition;
//search for word in Struct array
while (inStructArray == false){
for(int i=0; i<aSize; i++){
if (theWord == theStructArray[i].word){
inStructArray = true;
inStructPosition = i;
}
}
break;
}
//if word is in struct array
if (inStructArray == true){
theStructArray[inStructPosition].count++;
}
//else if it isn't
else{
//create new wordItem and add into struct
wordItem newWord;
newWord.word = theWord;
newWord.count = 1;
theStructArray[counter+(100*doubleCount)] = newWord;
counter++;
}
//if struct array hits maximum amount of elements,
if (counter == (aSize-1)){
doubleArray(theStructArray, aSize);
counter = 0;
doubleCount++;
aSize +=100;
}
}
}
}
inFile.close();
}
//Bubble sort masterArray
int bI, bJ, flag = 1;
wordItem bTemp;
for(bI=1; (bI <= aSize && flag); bI++){
flag = 0;
for(bJ=0; bJ<aSize; bJ++){
if(theStructArray[bJ+1].count > theStructArray[bJ].count){
bTemp = theStructArray[bJ];
theStructArray[bJ] = theStructArray[bJ+1];
theStructArray[bJ+1] = bTemp;
flag = 1;
}
}
}
//Print topN words
printTopN(theStructArray, atoi(argv[1]));
//print others
cout << "#" << endl;
cout << "Array doubled: " << doubleCount << endl;
cout <<"#" << endl;
cout << "Unique non-common words: "<< (aSize-100+counter)<<endl;
cout << "#"<<endl;
cout <<"Total non-common words: "<< getTotalNumberNonCommonWords(theStructArray, aSize, counter)<<endl;
return 0;
}
void getStopWords(char *ignoreWordFileName, vector<string>& _vecIgnoreWords){
ifstream inFile(ignoreWordFileName);
if(inFile.is_open()){
int a = 0;
string line;
while(getline(inFile, line)){
_vecIgnoreWords.insert(_vecIgnoreWords.begin() + a, line);
}
inFile.close();
}
return;
}
bool isCommonWord(string word, vector<string>& _vecIgnoreWords){
for(int i=0; i<STOPWORD_LIST_SIZE; i++){
if(word == _vecIgnoreWords.at(i)){
return true;
}
}
return false;
}
void printTopN(wordItem wordItemList[], int topN){
cout << endl;
for(int i=0; i<topN; i++){
cout<< wordItemList[i].count << '-' << wordItemList[i].word << endl;
}
return;
}
void doubleArray(wordItem wordItemList[], int size){
wordItem *tempArray = new wordItem[size+100];
for(int i=0; i<size; i++){
tempArray[i] = wordItemList[i];
}
delete [] wordItemList;
wordItemList = tempArray;
}
int getTotalNumberNonCommonWords(wordItem wordItemList[], int size, int wordCount){
int total = 0;
for(int i=0; i<(size-100+wordCount); i++){
total+=wordItemList[i].count;
}
return total;
}
You are doing very bad things in void doubleArray(wordItem wordItemList[], int size)
you can call delete [] on the array if you pass an array, but you cannot change its value, so doubleArray(theStructArray, aSize); will cause theStructArray to be deleted but not assigned to the memory you allocated. You are just assigning the local variable in the function doubleArray
It is similar to:
void doubleit(int x)
{
x *= 2;
}
int y=3;
doubleit(y);
here x was momentarily doubled to 6, but y never changed.
you need to use references, or better make theStructArray a std::vector and be done with it.
This program reads information from a text file, stores it in an array, and performs one of 3 functions. I need to be able to check if the array is full and double the size if it is or half the size if their is a deletion 1/4 of the size of the array. Tried to be brief so if you need more information, let me know.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct info{
char letter;
string SSN;
string firstName;
string lastName;
};
void insertion(int &count, int &validInsertationCount, string &SSN, char &letter, string &firstName, string &lastName, info *list);
void deletion(int &count, int &validDeletionCount, string &SSN, char &letter, string &firstName, string &lastName, info *list);
void retrieval(int &count, int &validRetrievalCount, string &SSN, string &firstName, string &lastName, info *list);
int main(int argc, char* argv[]){
int arraySize = 1000;
struct info list[1000];
fstream input(argv[1]);
int count = 0;
int validInsertationCount = 0;
int validDeletionCount = 0;
int validRetrievalCount = 0;
while(!input.eof()){
input >> list[count].letter >> list[count].SSN >> list[count].firstName >> list[count].lastName;
if(list[count].letter == 'i'){
insertion(count, validInsertationCount, list[count].SSN, list[count].letter, list[count].firstName, list[count].lastName, list);
}
else if(list[count].letter == 'd'){
deletion(count, validDeletionCount, list[count].SSN, list[count].letter, list[count].firstName, list[count].lastName, list);
}
else if(list[count].letter == 'r'){
retrieval(count, validRetrievalCount, list[count].SSN, list[count].firstName, list[count].lastName, list);
}
count++;
}
input.close();
int numberOfItems = validInsertationCount - validDeletionCount;
cout << "The Number of Valid Insertation: " << validInsertationCount << endl;
cout << "The Number of Valid Deletion: " << validDeletionCount << endl;
cout << "The Number of Valid Retrieval: " << validRetrievalCount << endl;
cout << "Item Numbers in the array: " << numberOfItems << endl;
cout << "Array Size is: " << arraySize << endl;
//cout << "Time Elapsed: " << <<endl;
}
void insertion(int &count, int &validInsertationCount, string &SSN, char &letter, string &firstName, string &lastName, info *list){
for(int i = 0; i < count; i++){
if(SSN == list[i].SSN && list[i].letter == 'i'){
for(int k = i; k < count; k++){
list[k].SSN = list[k+1].SSN;
list[k].letter = list[k+1].letter;
list[k].firstName = list[k+1].firstName;
list[k].lastName = list[k+1].lastName;
}
count--;
return;
}
}
validInsertationCount++;
return;
}
void deletion(int &count, int &validDeletionCount, string &SSN, char &letter, string &firstName, string &lastName, info *list){
for(int i = 0; i < count; i++){
if(SSN == list[i].SSN && firstName == list[i].firstName && lastName == list[i].lastName){
for(int k = i; k < count; k++){
list[k].SSN = list[k+1].SSN;
list[k].letter = list[k+1].letter;
list[k].firstName = list[k+1].firstName;
list[k].lastName = list[k+1].lastName;
}
count--;
validDeletionCount++;
return;
}
}
}
void retrieval(int &count, int &validRetrievalCount, string &SSN, string &firstName, string &lastName, info *list){
for(int i = 0; i < count; i++){
if(SSN == list[i].SSN && firstName == list[i].firstName && lastName == list[i].lastName){
validRetrievalCount++;
}
}
return;
}
You can't simply resize a statically allocated array so you'll want to either use std::vector or malloc/new to allocate a dynamic array. However in that case you can't determine the size of the array using sizeof(). So you either keep a size variable or use a "delimiter" value in order to pinpoint the end of the array.
I have been attempting to find a way to sort an array of pointers (pointing to strings) and then display the non-sorted list and the sorted list but no mater what I try the 2nd printed list is always identical to the original non-sorted list. Any help you could offer would be greatly appreciated (and I'm sorry if my code is a mess I'm a new student)
this is my main(lab5.cpp)
#include <cstdlib>
#include <iostream>
#include "student.h"
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
student stu;
stu.list();
system("PAUSE");
return EXIT_SUCCESS;
}
This is my header(student.h)
#include <string>
class student
{
public:
student( );
void setnameage();
int getage(int);
std::string getname(int);
void sort();
void list();
private:
std::string name[50];
std::string nameL[50];
int age[50];
std::string * Pname ;
int * Page;
int amount;
};
This is my object (student.cpp)
#include <iostream>
#include <iomanip>
#include "student.h"
#include <string>
using namespace std;
//constructor
student::student()
{
int i = 0;
amount = 0;
Pname = name;
Page = age;
while (i != 50)
{
age[i] = 0;
name[i] = "A";
i = i +1 ;
}
std::cout << "Enter number of students(max 50) \n" << ">";
std::cin >> amount;
}
//sets the neame and the age
void student::setnameage()
{
int i = 0;
while (i != amount)
{
std::cout << "Enter name " << i+1 <<" (last, first):";
std::cin >> name[i] >> nameL[i];
std::cout << "enter age";
std::cin >> age[i];
i++;
}
}
//get age
int student::getage(int i)
{
return age[i];
}
//get name
std::string student::getname(int i)
{
return name[i];
}
//sorts the aray of pointers
void student::sort()
{
std::string tempL;
int tempN;
i = 0
for (int i = 1; i <= amount-1; i++)
{
for(int j=i+1; j <= amount; j++)
{
if(Pname[i].compare(Pname[j]) > 0)
{
tempN = Page[i];
Page[i] = Page[j];
Page[j] = tempN;
// tempL = Pname[i];
Pname[i].swap(Pname[j]);
//Pname[j] = tempL;
}
}
}
}
//displayes the final results
void student::list()
{
setnameage();
int i = 0;
std::cout << "original list\n-------------";
while(i != amount)
{
std::cout<< "\n" << getname(i) << ">" << getage(i);
i++;
}
sort();
i = 0;
std::cout << "\nAlphabetized list\n-------------";
while(i != amount)
{
std::cout<< "\n" << Pname[i] << ">" << Page[i];
i++;
}
}
First let me say your program has a lot of design problems, but to answer your actual question:
The trouble is you don't have an array of 50 pointers, you just have one pointer to the start of the array. In your sort function you have this line to swap the string pointers:
Pname[i].swap(Pname[j]);
But this doesn't swap the pointers, it swaps the original strings. So instead of ending up with the original array of strings, and a re-ordered array pointing to those strings, you just end up with an array of re-ordered strings.
You should change std::string* pName; to std::string* pName[50];. At the start of your program, initialise the array to point to the strings.
for (int i = 0; i < 50; i++) pName[i] = &name[i];
Then in your sort function you should use std::swap() to swap the pointers themselves:
std::swap(pName[i], pName[j]);
Finally, since pName[i] is now a pointer, whenever you actually want to access the string you have to dereference the pointer. For example,
if(Pname[i].compare(Pname[j]) > 0)
becomes
if(Pname[i]->compare(*Pname[j]) > 0)
The same problem exists with your method of sorting the ages.
A much better design for your program would be to use std::list<std::pair<std::string, int>> to store the names and ages. Then you can use the built in sorting functions to sort the list (and easily make a copy of it if you need to keep the original as well).