Thread: Incorrect Valules displaying in a struct member

  1. #1
    Registered User
    Join Date
    Mar 2006
    Posts
    30

    Incorrect Valules displaying in a struct member

    My program is supposed to read input ( a student ID and then a score, separated by a space) from a user named file, store that data in a struct, and compute the average of all the scores. If the student's score is 10 points above or below the average, then the string Satisfactory is supposed to store in gradeString. If the score is more than 10 points above the average, then the string outstanding should be stored in gradeString, and if the students' score is more than 10 points below average then unsatisfactory should be stored in gradeString. Then another function prints out the ID, the score, and the gradeString. My problem is that the score does not seem to be read properly. When I run the program, the score shows up for every student as -10 and the gradeString that is printed is Satisfactory. First of all, I cannot figure out what I am doing wrong as far as reading in the score. Isn't the white space between the ID and score skipped and the next input read from the next character?

    I checked the average by doing cout in that function, and that looks like it is calculated correctly. So I am not sure why every gradeString is not stored as unsatisfactory since all the scores are -10 which is definitely lower than 'more than 10 points below the average'.

    Here is my code. If you could help me figure out where I went wrong, I would greatly appreciate it.
    Code:
     /* The format of the input data file is multiple lines, each containing an ID number followed by a score:
    
        12345 90
        23456 100
        31245 66
        ......
        */
    
        /* The program structure is as follows:
    
        main
          openInputFile
          populateArrayOfStructures
             computeAverage
             populateGradeField
          printTable
    
        */
    
        #include <iostream>
        #include <string>
        #include <fstream>
        #include <iomanip>
    
        using namespace std;
    
    
        struct StudentData
        {
          int id;
          int score;
          string gradeString;
        };
    
    
        const int MAX_SIZE = 21; // size of array
        const bool DEBUG = true; // used during testing
        //const bool DEBUG = false; // used during production
    
        void populateArrayOfStructures (ifstream& ins, StudentData data[], int& count, bool& tooMany);
        float computeAverage (StudentData data[], int count);
        void printTable (StudentData data[], int count);
        void populateGradeField (StudentData data[], int count, float average);
        void openInputFile (ifstream& ins);
    
        void main()
        {
          ifstream ins;
          StudentData data[MAX_SIZE];
          int count;
          bool tooMany;
    
          openInputFile(ins);
          populateArrayOfStructures (ins, data, count,tooMany);
          if (count <= 0)
          {
          	cout << "No items read from file" << endl;
          	cout << "Exiting program." << endl;
          	exit(0);
          }
          if (DEBUG)
          {
        	cout << "Items read from file:" << count << endl;
        	cout << "Value of tooMany: " << tooMany << endl;
          }
          if (tooMany)
          {
        	cout <<"There are more than " << MAX_SIZE << " items in file." << endl;
          }
          if (DEBUG) cout << "Processing " << count << " items from the input file." << endl;
    
          printTable (data, count);
        }
    
    
    
        /*
           This function uses a loop to populate the id and score fields of the array. It then calls computeAverage to get the
           average score and then calls populateGradeField to populate the gradeString fields in the array.
    
           The function also calculates and places values in the count and tooMany OUT parameters.
        */
        void populateArrayOfStructures (ifstream& ins, StudentData data[], int& count, bool& tooMany)
        {
    		float average = 0;
    		count = 0;
    		
    	for (int i = 0; i < MAX_SIZE; i++)
    	{
    		ins >> data[i].id >> data[i].score;
    		count++;
    	}
    	if (DEBUG)
          {
        	cout << "count:" << count << endl;
         }
    		computeAverage(data, count);
    		populateGradeField(data, count, average);
    	if (count > MAX_SIZE)
    	{
    		tooMany = true;
    	}
    
    
        }
    
    
    
        /*
            This function simply displays the data in the array of structures on the screen.
        */
        void printTable (StudentData data [], int count)
        {
    		for (int i = 0; i < MAX_SIZE; i++)
    		{
    			cout << data[i].id << ' ' << data[i].score << ' ' << data[i].gradeString << '\n';
    		}
    
    	
    
        }
    
    
        /*
            This function compares the average with each score in the array and populates the gradeString fields in the array.
        */
        void populateGradeField (StudentData data[], int count, float average)
        {
    
    		for (int i = 0; i < MAX_SIZE; i++)
    		{
    			if ((data[i].score = (int(average) - 10)) || (data[i].score = (int(average)+ 10)))
    			{
    				data[i].gradeString = "Satisfactory";
    			}
    			if ((data[i].score > (int(average) + 10)))
    			{
    				data[i].gradeString = "Oustanding";
    			}
    			if ((data[i].score < (int(average) - 10)))
    			{
    				data[i].gradeString = "Unsatisfactory";
    			}
    			
    		} 
    
    	
    
        }
    
    
        /*
            Based on the scores in the array, this function calculates and returns the average
        */
        float computeAverage (StudentData data[], int count)
        {
    	int sum = 0;
    	float average = 0;
    
    	for (int i = 0; i < MAX_SIZE; i++)
    		{
    			sum += data[i].score;
    		}
    		average = float(sum) / float (count);
    	if (DEBUG)
          {
        	cout << "average:" << average << endl;
         }	
    
    			
          	return average;
        }
    
    
        /*
           This function prompts the user for the input file name and then attempts to open that file. If the
           file cannot be opened the function displays a message and terminates the program.
        */
        void openInputFile (ifstream& ins)
        {
    	string fileName;
    
      	cout << "Enter name of the first input file" << endl;
     	cin >> fileName;
    
      	ins.open (fileName.c_str());
      	if (ins.fail())
      		{
    			cout << "Could not open file " << fileName << endl;
    			cout << "Exiting program." << endl;
    			exit (0);
      		}
    
        }

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    1. You declare main as void, this is wrong. Main returns an int, always has done.
    See the FAQ.

    2. You ignore the return result of your computeAverage() call.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> if ((data[i].score = (int(average) - 10)) || (data[i].score = (int(average)+ 10)))

    This is another important problem. Remember the difference between assignment and testing for equality.


    The rest of your code might break if the input file has less than MAX_SIZE entries. When reading in ids and scores, you need to check the return value of the read and break the loop if the read fails. Also, you want to use count, not MAX_SIZE, in the for loops that loop through the array everywhere except the loop that first populates the array.

  4. #4
    Registered User
    Join Date
    Mar 2006
    Posts
    30
    Quote Originally Posted by Salem View Post
    1. You declare main as void, this is wrong. Main returns an int, always has done.
    See the FAQ.

    2. You ignore the return result of your computeAverage() call.

    The problem is that my instructor put together the bare bones, and I am afraid to change the return type of main, because that is what he put in there.
    I made some changes, and now the scores are showing up correctly, but the gradeString is only printing out intermittently.
    Code:
    #include <iostream>
        #include <string>
        #include <fstream>
        #include <iomanip>
    
        using namespace std;
    
    
        struct StudentData
        {
          int id;
          int score;
          string gradeString;
        };
    
    
        const int MAX_SIZE = 21; // size of array
        const bool DEBUG = true; // used during testing
        //const bool DEBUG = false; // used during production
    
        void populateArrayOfStructures (ifstream& ins, StudentData data[], int& count, bool& tooMany);
        float computeAverage (StudentData data[], int count);
        void printTable (StudentData data[], int count);
        void populateGradeField (StudentData data[], int count, float average);
        void openInputFile (ifstream& ins);
    
        void main()
        {
          ifstream ins;
          StudentData data[MAX_SIZE];
          int count;
          bool tooMany;
    
          openInputFile(ins);
          populateArrayOfStructures (ins, data, count,tooMany);
          if (count <= 0)
          {
          	cout << "No items read from file" << endl;
          	cout << "Exiting program." << endl;
          	exit(0);
          }
          if (DEBUG)
          {
        	cout << "Items read from file:" << count << endl;
        	cout << "Value of tooMany: " << tooMany << endl;
          }
          if (tooMany)
          {
        	cout <<"There are more than " << MAX_SIZE << " items in file." << endl;
          }
          if (DEBUG) cout << "Processing " << count << " items from the input file." << endl;
    
          printTable (data, count);
        }
    
    
    
        /*
           This function uses a loop to populate the id and score fields of the array. It then calls computeAverage to get the
           average score and then calls populateGradeField to populate the gradeString fields in the array.
    
           The function also calculates and places values in the count and tooMany OUT parameters.
        */
        void populateArrayOfStructures (ifstream& ins, StudentData data[], int& count, bool& tooMany)
        {
    		float average = 0;
    		count = 0;
    		
    	for (int i = 0; i < MAX_SIZE; i++)
    	{
    		ins >> data[i].id >> data[i].score;
    		count++;
    	}
    	if (DEBUG)
          {
        	cout << "count:" << count << endl;
         }
    		average = computeAverage(data, count);
    		populateGradeField(data, count, average);
    	if (count > MAX_SIZE)
    	{
    		tooMany = true;
    	}
    
    
        }
    
    
    
        /*
            This function simply displays the data in the array of structures on the screen.
        */
        void printTable (StudentData data [], int count)
        {
    		for (int i = 0; i < count; i++)
    		{
    			cout << data[i].id << ' ' << data[i].score << ' ' << data[i].gradeString << '\n';
    		}
    
    	
    
        }
    
    
        /*
            This function compares the average with each score in the array and populates the gradeString fields in the array.
        */
        void populateGradeField (StudentData data[], int count, float average)
        {
    
    		for (int i = 0; i < count; i++)
    		{
    			if ((data[i].score == (int(average) - 10)) || (data[i].score == (int(average)+ 10)))
    			{
    				data[i].gradeString = "Satisfactory";
    			}
    			if ((data[i].score > (int(average) + 10)))
    			{
    				data[i].gradeString = "Oustanding";
    			}
    			if ((data[i].score < (int(average) - 10)))
    			{
    				data[i].gradeString = "Unsatisfactory";
    			}
    			
    		} 
    
    	
    
        }
    
    
        /*
            Based on the scores in the array, this function calculates and returns the average
        */
        float computeAverage (StudentData data[], int count)
        {
    	int sum = 0;
    	float average = 0;
    
    	for (int i = 0; i < count; i++)
    		{
    			sum += data[i].score;
    		}
    		average = float(sum) / float (count);
    	if (DEBUG)
          {
        	cout << "average:" << average << endl;
         }	
    
    			
          	return average;
        }
    
    
        /*
           This function prompts the user for the input file name and then attempts to open that file. If the
           file cannot be opened the function displays a message and terminates the program.
        */
        void openInputFile (ifstream& ins)
        {
    	string fileName;
    
      	cout << "Enter name of the first input file" << endl;
     	cin >> fileName;
    
      	ins.open (fileName.c_str());
      	if (ins.fail())
      		{
    			cout << "Could not open file " << fileName << endl;
    			cout << "Exiting program." << endl;
    			exit (0);
      		}
    
        }
    And can someone tell me why the value of the bool variable tooMany is printing as 204?

    Here are the results of the above code, using an input file that has 22 items.
    Enter name of the first input file
    grades.txt
    count:21
    average:83.7143
    Items read from file:21
    Value of tooMany: 204
    There are more than 21 items in file.
    Processing 21 items from the input file.
    123456 90
    234561 100 Oustanding
    312452 66 Unsatisfactory
    456785 79
    951247 80
    487912 71 Unsatisfactory
    954852 85
    100001 90
    100000 99 Oustanding
    100003 67 Unsatisfactory
    100004 90
    100005 78
    100006 94 Oustanding
    100007 84
    100008 96 Oustanding
    100009 81
    100010 93 Satisfactory
    100011 79
    100012 76
    100013 87
    100041 73 Satisfactory

  5. #5
    Registered User
    Join Date
    Mar 2006
    Posts
    30
    Quote Originally Posted by Daved View Post
    The rest of your code might break if the input file has less than MAX_SIZE entries. When reading in ids and scores, you need to check the return value of the read and break the loop if the read fails.
    I am not sure what you mean, could you please explain a little more? Do you mean I should have an if clause involving count and if it's less than MAX_SIZE it should stop the loop?

  6. #6
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    I would recommend something like
    Code:
    if( data[i].score > average + 10 )
      data[i].gradestring = "Outstanding";
    else if( data[i].score < average - 10 )
      data[i].gradestring = "Unsatisfactory";
    else
      data[i].gradestring = "Satisfactory";
    That seems a little more rigorous than what you have at the moment. You're just trying to find out if a score lies in a range of percentages.

    if ((data[i].score == (int(average) - 10)) || (data[i].score == (int(average)+ 10)))

    Anything that passes this test is really great or .........

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> I am not sure what you mean, could you please explain a little more?
    In your test case, there are 21 entries, so your code will work. However, if there are less than (or more than) 21 entries then you will have problems. This is the code you use to read in the data:
    Code:
    	for (int i = 0; i < MAX_SIZE; i++)
    	{
    		ins >> data[i].id >> data[i].score;
    		count++;
    	}
    Notice how the only way this loop will stop is when i reaches MAX_SIZE (which is 21). No matter what in that code, count will end up being 21. What if there are only 10 entries in the file? The code will keep trying to read in data even after there is no more left. What if there are 25 entries in the file? The code will skip the last 4.

    The assignment should have given you a maximum number of entries in the file. My guess is that it isn't 21, it's something bigger (like 100 or something). That is what you should use for MAX_SIZE. Then in the for loop above, you should add an extra if that checks to see if the read succeeded. A simple way to do that is to put the read code into the if:
    Code:
    		if (!(ins >> data[i].id >> data[i].score))
    		{
    			// The read failed, so the input is done. Handle appropriately here.
    		}
    In other parts of the code you loop through the values in the array. It looks like you have correctly changed those loops to use count instead of MAX_SIZE. That is correct and makes more sense.

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > And can someone tell me why the value of the bool variable tooMany is printing as 204?
    Not all paths through your code assign a value, so you get whatever random junk is in memory. Initialise it to false.

    > The problem is that my instructor put together the bare bones, and I am afraid to change the return type of main
    I would suggest you change it. If they query it, then point them to here.
    http://www.research.att.com/~bs/bs_faq2.html#void-main
    I mean, if the inventor of the language says it's wrong then what else is there?
    If they've still got an attitude, then you're probably on the wrong course because you're going to end up learning a whole bunch of crap from an idiot that no longer cares because he's now got your money.
    At the very least, you'll need to do a whole lot of verification of everything they say.

    As you've seen, you can compile all sorts of programs which don't work, and the whole "well my compiler accepts it" argument just doesn't wash in the long run.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need help with linked list sorting function
    By Jaggid1x in forum C Programming
    Replies: 6
    Last Post: 06-02-2009, 02:14 AM
  2. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  3. Assignment HELP!!
    By cprogrammer22 in forum C Programming
    Replies: 35
    Last Post: 01-24-2009, 02:24 PM
  4. Bi-Directional Linked Lists
    By Thantos in forum C Programming
    Replies: 6
    Last Post: 12-11-2003, 10:24 AM
  5. Contest Results - May 27, 2002
    By ygfperson in forum A Brief History of Cprogramming.com
    Replies: 18
    Last Post: 06-18-2002, 01:27 PM