1

I'm sorry this is a repeat question, but no solutions seem to work for my code.

This is for an assignment in school on reading from a file and copying the data to an array. An exception is thrown every time I try to edit the array "arr" in main.

Here's my code:

#include <iostream>
#include <fstream>

using namespace std;

struct Student {
    string name;
    float gpa;
    int id;
};

void PrintStudents(Student arr[], int nstudents) {
    for (int i = 0; i < nstudents; i++) {
        cout << "Student name: " << arr[i].name << endl;
        cout << "Student GPA: " << arr[i].gpa << endl;
        cout << "Student ID: " << arr[i].id << endl;
    }
}

int ReadStudents(string fname, Student arr[]) {
    ifstream file;
    file.open(fname);

    int counter = 0;
    string name_local;
    float gpa_local;
    int id_local;

    int index = 0;

    while (!file.eof()) {
        if (counter == 0) {
            file >> name_local;
        }
        else if (counter == 1) {
            file >> gpa_local;
        }
        else if (counter == 2) {
            file >> id_local;
        }

        counter++;

        if (counter == 3) {
            counter = 0;
            Student newStudent = { name_local, gpa_local, id_local };
            arr[index] = newStudent;
            index++;
        }
    }

    file.close();
    return index;
}

void fillStudentArray(Student array[], int array_size) {
    Student temp = { "", 0, 0 };
    for (int i = 0; i < array_size; i++) {
        array[i] = temp;
    }
    return;
}

int main() {
    Student arr[128];
    fillStudentArray(arr, 128); // exception thrown here??
    cout << "Array filled." << endl;

    cout << "Reading students" << endl;
    int nstudents = ReadStudents("csci10.hw8.students.txt", arr);
    PrintStudents(arr, nstudents);

    return 0;
}

Thanks for any help! I'm totally stumped.

Edit: Woah, I left for a 30 minute coffee break and came back to a ton of answers! I'll try to respond to all of them.

Edit 2: Just got a solution! I was working in VS 2019, switched to old school terminal G++ and it worked! Thanks everyone for all the answers :)

Minighost
  • 63
  • 5
  • 1
    Maybe has something to do with [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) The whole loop could be simplified, no need for a counter. – churill Nov 18 '19 at 17:10
  • The loop looks all right, but I do wonder the wisdom of setting all 128 elements to the same object reference. – Robert Harvey Nov 18 '19 at 17:10
  • 1
    @RobertHarvey It's making a copy. – NathanOliver Nov 18 '19 at 17:10
  • Of the array, or the temp object? – Robert Harvey Nov 18 '19 at 17:11
  • Of the temp object – NathanOliver Nov 18 '19 at 17:11
  • Even though the declaration is not inside the loop? – Robert Harvey Nov 18 '19 at 17:12
  • 2
    `fillStudentArray` Looks fine to me. One problem is you do not check the file was successfully opened. – zdf Nov 18 '19 at 17:12
  • 1
    @RobertHarvey Yes. C++ uses value semantics by default. This just fills the array with 128 copies of an "empty" struct. – NathanOliver Nov 18 '19 at 17:12
  • Pretty much every access violation is it's own thing, so you'll rarely find an exact duplicate, but usually they fall into broad families. – user4581301 Nov 18 '19 at 17:13
  • 1
    @OP replace your while loop in `ReadStudents` with `while (file >> name_local >> gpa_local >> id_local) { arr[index++] = { name_local, gpa_local, id_local }; }`. Does that fix the problem? – NathanOliver Nov 18 '19 at 17:15
  • 2
    Unrelated: if you gave `Student` a default constructor that produced an "empty" student, there would be no need for `fillStudentArray`. Creating the array would do the deed for you – user4581301 Nov 18 '19 at 17:15
  • @NathanOliver: I don't think the code is getting that far anyway. The exception is being thrown in the second line of code in main. – Robert Harvey Nov 18 '19 at 17:15
  • 1
    @RobertHarvey It does [here](http://coliru.stacked-crooked.com/a/65b087a44547bbeb). Most likely it is the reading that is the issue. – NathanOliver Nov 18 '19 at 17:16
  • Things look right to me too provided that input file is correctly formatted, and there is actually a file there. If no file, things still run but will crash somewhere inside `ReadStudents`. Weird to me why exception thrown in second line in `main`, I didn't face the same issue. Different compiler used might be? – haxpor Nov 18 '19 at 17:31
  • Woah, uh, lots of answers. Er... I did the "fillStudentArray" function because a different thread recommended initializing the array instead of just declaring it. It didn't work, but I left it there just in case. NathanOliver-ReinstateMonica, Robert is correct... the exception is thrown on the second line of main. haxpor, I have no idea. I'm gonna try a different compiler (I'm in VS 2019) when I can. Thanks everyone for the answers! – Minighost Nov 18 '19 at 17:39

2 Answers2

1
  1. You do not check the file was successfully opened. Try this:

    ifstream file( fname );
    if ( !file )
      return -1;
    
  2. You do not need local variables for reading. Read directly in your array elements:

    file >> arr[index].name
    
  3. ReadStudents ignores the size of the passed array: you might get in trouble if you read more than the allocated size (read this again). You might use std::vector, if allowed. Alternatively, pass the size, too – the same way you did for fill.

  4. The way you are trying to read from file is overly complicated. Try a more c++ approach:

    • Define an extraction operator for Student:

      std::istream& operator>>( std::istream& is, Student& s )
      {
        return is >> s.name >> s.gpa >> s.id;
      }
      
    • Use it like you would use it for reading an integer:

      file >> arr[ index ]
      

      Alternatively you could use:

      is >> arr[ index ].name >> arr[ index ].gpa >> arr[ index ].id
      

You will get something like this:

int ReadStudents( string fname, Student arr[]/*how large is the array?*/ )
{
  ifstream file( fname );
  if ( !file )
    return -1;

  int index = 0;
  while ( file >> arr[ index ].name >> arr[ index ].gpa >> arr[ index ].id )
    index++;
  return index;
}
zdf
  • 3,363
  • 2
  • 14
  • 25
  • Er, I don't understand all of that. I get the file >> arr[index] though, I'll have to try that. I'll check if the file opens successfully as well. What is !file? "not file"? – Minighost Nov 18 '19 at 17:37
  • `!file` means _"not successfully opened"_. – zdf Nov 18 '19 at 17:39
  • It looks like it opens the file fine and reads it correctly, I just can't write to my array. Another user suggested a different compiler, so I'm going to try that next. – Minighost Nov 18 '19 at 17:45
  • 1
    My old code doesn't work in G++, but your code does! Thanks for your help, now I just need to figure out how it all works... – Minighost Nov 18 '19 at 17:48
  • I am using the same compiler as you: no exceptions thrown on the second line. – zdf Nov 18 '19 at 17:48
  • Huh, that's odd. Are read access violation exceptions common? – Minighost Nov 18 '19 at 17:52
  • No, they are an indication you did something wrong. It is very unlikely to be a compiler problem. Check the size of your array against the number of records in the file. The array must be large enough to accommodate all records. Also, if you are using the debugger, check the build configuration - it must be Debug, not Release. In Release mode the optimizations makes it, sometimes, hard to understand what happens. – zdf Nov 18 '19 at 18:00
  • There are dozens of different distributions of mingw and hundreds of different versions, so saying "I am using the same compiler" gets messy. – user4581301 Nov 18 '19 at 20:01
  • @user4581301 MSVC 2019 - the same as OP. – zdf Nov 18 '19 at 20:06
  • I have no clue how I got mingw out of that. Probably my brain scrambled the asker's name. – user4581301 Nov 18 '19 at 20:14
  • @user4581301 It happens. :o) – zdf Nov 18 '19 at 20:16
  • Sorry for the late response! VS 2019 still not working for me... The array is way larger than it needs to be (64 elements allocated, only 9 elements in the file being read). VS has a big green button at the top labelled "Windows Local Debugger" and I just use that to compile / run. – Minighost Nov 20 '19 at 22:23
  • You may remove the `fillStudentArray` - it is not needed - but my guess is the problem is located in some other part of your program and you misinterpreted the origin of the exception. In the original program you do not check the stream, so the loop will never exit, thus going beyond the end of the array - which triggers an exception. – zdf Nov 20 '19 at 22:47
1

Explanation

If the stream does not get opened successfull, you get an endless loop. You never check if the reading operations are successfull. If they are not you never reach eof but keep incrementing index and write to array indices out of bounds. Also the counter is superfluos here.

Furthermore I would suggest to use a std::vector instead of an array and use push_back(). This makes sure, you don't write out of bounds.

(Possible) solution

This loop:

while (!file.eof()) {
    if (counter == 0) {
         file >> name_local;
    }
    else if (counter == 1) {
        file >> gpa_local;
    }
    else if (counter == 2) {
        file >> id_local;
    }
    counter++;

    if (counter == 3) {
        counter = 0;
        Student newStudent = { name_local, gpa_local, id_local };
        arr[index] = newStudent;
        index++;
    }
}

should be changed (with the function definition) to:

int ReadStudents(string fname, std::vector<Student> &vec)
{
    // open stream, etc.

    while (file >> name_local >> gpa_local >> id_local) {
        Student newStudent = { name_local, gpa_local, id_local };
        vec.push_back(newStudent);
    }

    // cleanup
}

To explain a bit further what the while (file >> name_local >> gpa_local >> id_local) does:

Since std::ifstream::operator>> returns a reference to the stream itself, you can chain those statements together.

The last reference gets implicitly converted to bool (or void* in c++11 or earlier) as seen here. This evaluates true if the last reading operations where successfull (so name_local, gpa_local and id_local now have valid values) and the stream is ready for IO-operations (so it didn't reach eof while reading). This implies that it's also checking if the stream was opened at all.

Once those conditions are met you can create a new element and push it into the vector.

Community
  • 1
  • 1
churill
  • 9,299
  • 3
  • 13
  • 23