Initializer is overwriting second array - c++

I cannot solve this, I'm almost accepting the fact that it might be a memory issue with my machine at this point.
I have this initializer:
Search::Search(ifstream& inFile)
{
int id = 0;
int i = 0;
inFile >> id;
while (inFile) {
if(i < SEARCH_DATA_SIZE) {
SearchDataFirst[i] = id;
SearchDataFirstSorted[i] = id;
} else if(i >= SEARCH_DATA_SIZE) {
SearchDataLast[i] = id;
SearchDataLastSorted[i] = id;
}
i++;
inFile >> id;
}
}
And in my header i have private data like so:
const int SEARCH_DATA_SIZE = 20;
int SearchDataFirst[SEARCH_DATA_SIZE];
int SearchDataLast[SEARCH_DATA_SIZE];
int SearchDataFirstSorted[SEARCH_DATA_SIZE];
int SearchDataLastSorted[SEARCH_DATA_SIZE];
The initializer is getting the first 20 ints from inFile, storing them, and then going to the next records and storing those in separate arrays,
When I do a print of the arrays, SearchDataFirstSorted has the values of SearchDataLast, even though there is no possible way ever that this could happen. SearchDataLastSorted has weird funky numbers.
SearchedDataFirst is fine.
I have never been this frustrated with a programming language.
Hopefully you can help.
There is nothing else going on, only the initializer is being called at this point.

The problem is that if i >= SEARCH_DATA_SIZE, then SearchDataLast[i] is pointing outside SearchDataLast! What you really need is something like this:
for(int i = 0; i < SEARCH_DATA_SIZE; ++i)
{
inFile >> id;
SearchDataFirst[i] = id;
SearchDataFirstSorted[i] = id;
}
for(int i = 0; i < SEARCH_DATA_SIZE; ++i)
{
inFile >> id;
SearchDataLast[i] = id;
SearchDataLastSorted[i] = id;
}
That is, you need to reset i back to zero after you're done populating SearchDataFirst and before you start populating SearchDataLast.

Your logic doesn't make any sense. The best I can figure, you're trying to do something like this:
} else if(i >= SEARCH_DATA_SIZE) {
SearchDataLast[i - SEARCH_DATA_SIZE] = id;
SearchDataLastSorted[i - SEARCH_DATA_SIZE] = id;
}
in its current form every single element you try to write to in SearchDataLast and SearchDataLastSorted is beyond the array bounds (which does indeed lead to memory corruption).

Your problem is here:
else if(i >= SEARCH_DATA_SIZE) {
SearchDataLast[i] = id;
SearchDataLastSorted[i] = id;
}
You're going past the bounds of SearchDataLast and SearchDataLastSorted, since they're of size SEARCH_DATA_SIZE and i is greater than or equal to that amount. What you probably want is i - SEARCH_DATA_SIZE as the indexes.
Because of this you're causing undefined behaviour, where in your case the compiler has allocated memory for each of the arrays in order. So you're basically skipping SEARCH_DATA_SIZE elements in SearchDataLast, and storing the values you want there in SearchDataFirstSorted.

You don't ensure that you don't overrun the SearchDataLast and SearchDataLastSorted arrays.
When i gets beyond SEARCH_DATA_SIZE, you switch arrays, ...but... you continue indexing from SEARCH_DATA_SIZE which is bigger than the SearchDataLast array.
Presumably, you intended to write something like:
else if (i >= SEARCH_DATA_SIZE)
{
SearchDataLast[i-SEARCH_DATA_SIZE] = id;
SearchDataLastSorted[i-SEARCH_DATA_SIZE] = id;
}
You would still need to ensure that you do not overflow the end of these arrays (by ensuring that i is not 2 * SEARCH_DATA_SIZE or larger.

Related

How do i "connect" array elements with other variables?

So, I got this project that my teacher gave me and im pretty new so I came accross a little problem. Basically, I need to input a few citites and their population, and I need to output them in order from the one with the most people to the one with least people in it. I used structures to store information about each city and now I don't know how can I sort of "connect" population to each city if you know what I mean. Thank you!
This is what it looks like, and to people that didn't understand the question, I got the city population and sorted it from the highest to the lowest, and now I want to display the name of the city with it's population next to it but I don't know how can I now add them together.
struct city
{
string name;
int population;
}cityVar[10];
int main()
{
// Input
for (int i = 0; i < 5; i++)
{
cin >> cityVar[i].name;
cin >> cityVar[i].population;
}
int i, j;
int temp;
for (i = 0; i < 5; i++)
{
for (j = i + 1; j < 5; j++)
{
if (cityVar[i].population < cityVar[j].population)
{
temp = cityVar[i].population;
cityVar[i].population = cityVar[j].population;
cityVar[j].population = temp;
}
}
}
for (i = 0; i < 5; i++)
{
cout << cityVar[i].population << " ";
}
}
You do not connect two variables. You group them together when they belong to the same concept.
In this specific case I suggest you to create an abstraction for a city by creating a struct, and instruct it on how to to compare itself to other cities based solely (in this case) on the population.
You can do this by implementing the operator<
struct city{
unsigned population
bool operator<(const city& c1){
return this.population < c1.population;
}
};
You can then add your element into an array or even better a std::vector and sort them using std::sort.
Since this sounds like an homework, I will leave the rest to you. You should be able to continue on your own from here.
You lost the "connection" because you only swapped the populations when you should have swapped the entire cities:
city temp = cityVar[i];
cityVar[i] = cityVar[j];
cityVar[j] = temp;
Or, as we often write it
std::swap(cityVar[i], cityVar[j]);
You need to swap the full object, there are many ways to do this but depending on what you've been taught you'll need to be careful of which method you use. This is by no means an exhaustive list, I've only shown the simplest I can think of....
The simplest method (but requires another two lines) and depends on the data structure, so use a temp city instead of a temp int.
struct city temp;
:
:
:
if (cityVar[i].population < cityVar[j].population)
{
temp.population = cityVar[i].population;
temp.name = cityVar[i].name;
cityVar[i].population = cityVar[j].population;
cityVar[i].name = cityVar[j].name;
cityVar[j].population = temp.population;
cityVar[j].name = temp.name;
}
Another method would be to use a list of pointers for the sorted list
int main()
{
struct city *cityVarSorted[10];
for (int i = 0; i < 5; i++) // initialise list to point to current structs
{
cityVarSorted[i] = &cityVar[i];
}
:
:
struct city *temp;
:
if (cityVarSorted[i]->population < cityVarSorted[j]->population)
{
temp = cityVarSorted[i]; // we're swapping the pointer now and not the individual members
cityVarSorted[i] = cityVarSorted[j];
cityVarSorted[j] = temp;
}
}
of course that's if you've been taught pointers. You need to know how it works so you are able to explain it

C++ reading data from txt file and putting it into 2d array

I've got a file with numbers separated by a single space and I want to put them into the 2D array. There are 200 rows and 320 numbers each.
This is my code:
int data[200][320];
int i = 0;
int j = 0;
file.open("./../../../Data_PR2/data.txt", ios::in);
while (file>> data[i][j])
{
if (j == 319) {
j = 0;
i++;
} else
j++;
}
And it kinda works, because first rows are correctly inserted but not all rows.
So what's wrong?
A simpler method is to use / and % instead of if statement:
unsigned int count = 0;
unsigned int row = 0;
unsigned int column = 0;
while (file >> data[row][column])
{
++count;
column = count % 320;
row = count / 320;
}
Maybe a more efficient method is to treat the array as a single dimension array, since all slots are contiguous:
int * p_slot = &data[0][0];
while (file >> *p_slot)
{
++p_slot;
}
There are other methods, such as input iterators.
The above examples do not check for overflow. Overflow checking is left as an exercise for the reader. :-)
Note: This is not an optimization, but a simplification. Format conversion, out of bounds checking and the process of inputting, make optimizing moot. The biggest optimization would be reading bigger blocks into memory, then reading from memory; but for this size, it's not worthwhile.

Quicksort String Vector Alphabetically

I was having trouble with my code. I pass in an array of strings (names) and I want to do a quick sort and sort them alphabetically. Then, what I would like to do is with my array of years and ages, is swap those values respectively with the values swapped in my names array. However, I'm having trouble trying to implement that.
In the main function, I pass in:
quicksort(names, names[0], names[names.size() - 1]);
And in that code contains
void quicksort(vector<string> &names, string min, string max){
cout << "\n\tSorting names...\n";
int temp = 0,
i = 0;
string lowMin = max,
lowMax = min,
highMin = max,
highMax = min,
pivot;
vector<string> below,
above;
if (min != max){
pivot = (max[i] + min[i]) / 2;
while (temp < names.size()){
if (names[temp] <= pivot){
if (lowMax.compare(names[temp]) < 0){
lowMax = names[temp];
}
if (lowMin.compare(names[temp]) > 0){
lowMin = names[temp];
}
below.push_back(names[temp]);
}
else {
if (highMax.compare(names[temp]) < 0){
highMax = names[temp];
}
if (highMin.compare(names[temp]) > 0){
highMin = names[temp];
}
above.push_back(names[temp]);
}
temp++;
}
if ((below.size() > 1) && (names.size() != below.size())){
quicksort(below, lowMin, lowMax);
}
if ((above.size() > 1) && (names.size() != above.size())){
quicksort(above, highMin, highMax);
}
for (size_t i = 0; i < below.size(); i++){
names[i] = below[i];
}
for (size_t i = below.size(); i < names.size(); i++){
names[i] = above[i - below.size()];
}
}
} // // End quicksort()
In this case, would it be better to make a swap function and send in two integers so I can swap values in my other vector arrays? For example, I was thinking swapValue(int i, int j){ /* do something */}
Also, can someone explain to me the difference between foobar[i].swap(foobar[j]) and swap(foobar[i], foobar[j])? Are these methods more efficient than say creating a temp variable and swapping values?
Don't implement quicksort if you do it only because you need some sorting algorithm to use.
You seem to have three std::vector for name, age and year, where elements at the same position are related. Why not combine everything?
struct Person //or some name
{
std::string name;
int age;
int year;
int operator<(const Person &other) //comparison
{
return name.compare(other.name);
}
};
Of course, you could make a full class with the Big5 etc. too, if you want.
Now, with a vector<Person> v;, you can use std::sort:
std::sort(v.begin(), v.end());
That's all.
...If you still want to have a quicksort function, take eg. this, and change the lines with the swaps so that swaps are made on all three vectors.
About your other question:
The swap of std::string and the independent function swap with string paramters do the same thing (technically they don't have to, they are completely independent, but they do).
And why swap(a,b) is better than c=a;a=b;b=c;:
In the second code, values are copied three times. For std::string, this means three times allocating new memory and copying the whole content. Swap can do it without any content copy (it can access the internal pointers etc. and exchange only these).

Unsure of the condition for this loop (Pointer arithmetic)

So I'm supposed to edit a bunch of code via requirements in the comments. I've gotten most of it but one part in particular has me stuck. It loops through as the original did, but I can't figure out what the condition of the loop should be and whatever I put just ends up crashing. I think it's something with sizeof? but nothing I try works. Any help would be appreciated!
Original:
int main()
{
const string ID_BASE = "56-123-";
// CHANGE STATIC ARRAY INTO DYNAMIC ARRAY.
const int NUM_PRODUCTS = 5;
Product products[NUM_PRODUCTS];
int i;
//cout << "Enter number of toys: ";
//cin >> numProducts;
....Code to fill products...
//ALTER LOOP TO USE ONLY POINTER OPERATIONS
// (NO SUBSCRIPTS).
for (i = 0; i < numProducts; i++)
{
show(products[i]);
}
My changes:
int main()
{
const string ID_BASE = "56-123-";
// CHANGE STATIC ARRAY INTO DYNAMIC ARRAY.
int numProducts;
Product *products;
int i;
cout << "Enter number of toys: ";
cin >> numProducts;
products = new Product[numProducts];
...Code to fill products...
//ALTER LOOP TO USE ONLY POINTER OPERATIONS
// (NO SUBSCRIPTS).
for (Product *prodP = products; ; *prodP++)
{
show(*prodP);
}
Condition:
prodP !=products+numProducts;
Do it iterator style:
auto begin = products;
auto end = products + numProducts;
for( auto itr=begin; itr!=end; ++itr )
{
show(*itr);
}
Maybe I am missing something here, but when does the modified loop end??
for (Product *prodP = products; ; *prodP++)
{
show(*prodP);
}
there are only finite elements, and I think you are over-running the array.
Profile with valgrind for these kind of errors/suspected errors.

C++ Dynamic Array Inputs

I am using two dynamic arrays to read from a file. They are to keep track of each word and the amount of times it appears. If it has already appeared, I must keep track in one array and not add it into the other array since it already exists. However, I am getting blank spaces in my array when I meet a duplicate. I think its because my pointer continues to advance, but really it shouldn't. I do not know how to combat this. The only way I have was to use a continue; when I print out the results if the array content = ""; if (*(words + i) == "") continue;. This basically ignores those blanks in the array. But I think that is messy. I just want to figure out how to move the pointer back in this method. words and frequency are my dynamic arrays.
I would like guidance in what my problem is, rather than solutions.
I have now changed my outer loop to be a while loop, and only increment when I have found the word. Thank you WhozCraig and poljpocket.
Now this occurs.
Instead of incrementing your loop variable [i] every loop, you need to only increment it when a NEW word is found [i.e. not one already in the words array].
Also, you're wasting time in your inner loop by looping through your entire words array, since words will only exist up to index i.
int idx = 0;
while (file >> hold && idx < count) {
if (!valid_word(hold)) {
continue;
}
// You don't need to check past idx because you
// only have <idx> words so far.
for (int i = 0; i < idx; i++) {
if (toLower(words[i]) == toLower(hold)) {
frequency[i]++;
isFound = true;
break;
}
}
if (!isFound) {
words[idx] = hold;
frequency[idx] = 1;
idx++;
}
isFound = false;
}
First, to address your code, this is what it should probably look like. Note how we only increment i as we add words, and we only ever scan the words we've already added for duplicates. Note also how the first pass will skip the j-loop entirely and simply insert the first word with a frequency of 1.
void addWords(const std::string& fname, int count, string *words, int *frequency)
{
std::ifstream file(fname);
std::string hold;
int i = 0;
while (i < count && (file >> hold))
{
int j = 0;
for (; j<i; ++j)
{
if (toLower(words[j]) == toLower(hold))
{
// found a duplicate at j
++frequency[j];
break;
}
}
if (j == i)
{
// didn't find a duplicate
words[i] = hold;
frequency[i] = 1;
++i;
}
}
}
Second, to really address your code, this is what it should actually look like:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
//
// Your implementation of toLower() goes here.
//
typedef std::map<std::string, unsigned int> WordMap;
WordMap addWords(const std::string& fname)
{
WordMap words;
std::ifstream inf(fname);
std::string word;
while (inf >> word)
++words[toLower(word)];
return words;
}
If it isn't obvious by now how a std::map<> makes this task easier, it never will be.
check out SEEK_CUR(). If you want to set the cursor back
The problem is a logical one, consider several situations:
Your algorithm does not find the current word. It is inserted at position i of your arrays.
Your algorithm does find the word. The frequency of the word is incremented along with i, which leaves you with blank entries in your arrays whenever there's a word which is already present.
To conclude, 1 works as expected but 2 doesn't.
My advice is that you don't rely on for loops to traverse the string but use a "get-next-until-end" approach which uses a while loop. With this, you can track your next insertion point and thus get rid of the blank entries.
int currentCount = 0;
while (file)
{
// your inner for loop
if (!found)
{
*(words + currentCount) = hold;
*(frequency + currentCount) = 1;
currentCount++;
}
}
Why not use a std::map?
void collect( std::string name, std::map<std::string,int> & freq ){
std::ifstream file;
file.open(name.c_str(), std::ifstream::in );
std::string word;
while( true ){
file >> word; // add toLower
if( file.eof() ) break;
freq[word]++;
}
file.close();
}
The problem with your solution is the use of count in the inner loop where you look for duplicates. You'll need another variable, say nocc, initially 0, used as limit in the inner loop and incremented whenever you add another word that hasn't been seen yet.