954,498 Members — Technology Publication meets Social Media
Username:
Password:
Lost login information?
Have something to say? Contribute New Article Reply to this Article

Need help implementing a gradebook using dynamic memory

This is my first time using pointers and I'm having some trouble. So far I have declared a struct named Student which is meant to hold the data for each student. There is an input file with an unknown amount of entries. Each entry has a first and last name along with 7 scores.

I am trying to read in the data from the text file at this point. Then I will need to sort the data (selection sort) in alphabetical order by the students last names. And lastly output the data to a new text file.

Below I'll provide what I've done so far. Really I'm just trying to get a better insight as to how I should be approaching this problem. I can figure out the sorting and output later.

A sample entry from the input file:

ANTHONY CLARK  83  85  84  85  82  85  78
#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size=5;
    infile.open("input.txt");
    
    struct Student * data;
    data = new struct Student[size];

    for (int i = 0; i < size; i++ )
    {
        infile.getline(data[i].First,30);

    }


    


    infile.close();
    outfile.open("output.txt");
    outfile.close();
     



    delete [] data;
    

    return 0;
}
thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

Do not use getline here. The first name can be UP TO 30 characters, but will usually be less than that. Your getline stores other fields in the first name. This input file is separated by spaces and newlines, so it is tailor made for the >> operator instead of getline. I see no need to read in the data using getline.

for (int i = 0; i < size; i++ )
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> /* same for the rest */
    }
VernonDozier
Posting Expert
5,527 posts since Jan 2008
Reputation Points: 2,633
Solved Threads: 711
 

Thanks for the help. That is makes things much clearer. My next question is: How does the array continue to expand in order to span the length of the input file? I just did a simple print to the output file and it only displays the first 5 names on the list. I know that is because I set size = 5. What should I have done to make sure the array will continue to allocate memory?

#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size=5;
    infile.open("input.txt");
    
    struct Student * data;
    data = new struct Student[size];

    for (int i = 0; i < size; i++ )
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
               >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
               >> data[i].Score6 >> data[i].Score7;
    }


    


    infile.close();
    outfile.open("output.txt");

    for (int i = 0; i < size; i++ )
    {
    outfile << data[i].First << data[i].Last << " " << data[i].Score1
            << " " << data[i].Score2 << " " << data[i].Score3 << " "
            << data[i].Score4 << " " << data[i].Score5 << " "
            << data[i].Score6 << " " << data[i].Score7 << endl;
    }


    outfile.close();
     



    delete [] data;
    

    return 0;
}
thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

morning bump

thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 
Thanks for the help. That is makes things much clearer. My next question is: How does the array continue to expand in order to span the length of the input file? I just did a simple print to the output file and it only displays the first 5 names on the list. I know that is because I set size = 5. What should I have done to make sure the array will continue to allocate memory?

You have a few options.Pick an original size that you know will be large enough to handle anything you'll encounter (say 1,000,000) and size it to that initially. It's a waste of memory and bad coding practice, but resizing will be unnecessary. That's assuming that such a maximum exists in the first place.
Use a vector or something else that does the expanding and contracting for you.
Go through the file once and count how many records there are and size the array to that, then go through again to read the data.
Do an initial guess of the size, like you do with 5. Start reading the file. When you run out of room, resize the array manually by declaring a new array of the new size, deep copy the elements from the first array to the new array, then release the memory of the original array and reassign any pointers.
Use malloc, calloc, realloc, and free rather than new and delete in order to possibly avoid a deep copy - this is a C solution rather than a C++ solution.

I would imagine you are expected to implement approach 4 above. Here' a link where Narue comments.

http://www.daniweb.com/forums/thread13709.html

There are other examples too. Google "C++ resize dynamic array" or something similar.

VernonDozier
Posting Expert
5,527 posts since Jan 2008
Reputation Points: 2,633
Solved Threads: 711
 

Yea your correct. I need to dynamically expand the array as needed. I took a look at the link you gave me (thanks by the way) and am still having trouble implementing it correctly. So far this is what i have:

#include <fstream>
#include <iostream>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

int main()
{
    ifstream infile;
    ofstream outfile;
    int size = 5;
    infile.open("input.txt");
    
    struct Student * data;
    struct Student * temp;
    data = new struct Student[size];
    

    for (int i = 0; i < size; i++)
    {
        infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
               >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
               >> data[i].Score6 >> data[i].Score7;

        
        
    }
    
    temp = new struct Student[size * 2];

        for ( int i = 0; i < size; i++ )
        {
            temp[i] = data[i]; 
         }

        size *= 2;
        data = temp;

    for (int i = 0; i < size; i++)
    {
        infile >> temp[i].First >> temp[i].Last >> temp[i].Score1 >> temp[i].Score2
               >> temp[i].Score3 >> temp[i].Score4 >> temp[i].Score5
               >> temp[i].Score6 >> temp[i].Score7;
    }



    infile.close();
    outfile.open("output.txt");

    for (int i = 0; i < size; i++ )
    {
    outfile << data[i].Last << ", " << data[i].First << " " << data[i].Score1
            << " " << data[i].Score2 << " " << data[i].Score3 << " "
            << data[i].Score4 << " " << data[i].Score5 << " "
            << data[i].Score6 << " " << data[i].Score7 << endl;

    }

    delete [] data;
    delete [] temp;
    outfile.close();
     
    return 0;
}
thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

Define "still having trouble implementing it correctly". What is happening that shouldn't be happening or what isn't happening that should be happening?

VernonDozier
Posting Expert
5,527 posts since Jan 2008
Reputation Points: 2,633
Solved Threads: 711
 

I am unsure as to where I should be placing size *= 2. Its purpose is to double the size of the array each time it is necessary.

At first, I didn't know whether I should be placing the new array (temp) inside the loop which is pulling data from the input file. The link you posted was helpful. I am just having trouble applying it to my program correctly. The program as it is now will now display 10 items, which isn't the end product I am looking for.

What I would like to do is have this new array named temp to continue and allocate memory until the input file has been accounted for entirely. Am I on the right track by setting up the new temp array after the original for loop, and running a series of them on its own? Thanks again for your help.

thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

Give this a try.

ifstream infile;
ofstream outfile;
int size = 1;
    
infile.open("input.txt");
    
struct Student * data;
struct Student * temp;
data = new struct Student[size];

int i = 0;
while( !infile.eof() )
{
	infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;
        
	if( !infile.eof() )
	{
		size++, i++;
		temp = new struct Student[size];
		for( int c = 0; c < size; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}

infile.close();


I know this is constantly swapping between temp and data but its semi compact and gives the output I think you are looking for.

sfuo
Practically a Master Poster
656 posts since Jul 2009
Reputation Points: 164
Solved Threads: 99
 

I am unsure as to where I should be placing size *= 2. Its purpose is to double the size of the array each time it is necessary.

At first, I didn't know whether I should be placing the new array (temp) inside the loop which is pulling data from the input file. The link you posted was helpful. I am just having trouble applying it to my program correctly. The program as it is now will now display 10 items, which isn't the end product I am looking for.

What I would like to do is have this new array named temp to continue and allocate memory until the input file has been accounted for entirely. Am I on the right track by setting up the new temp array after the original for loop, and running a series of them on its own? Thanks again for your help.


Don't confuse size with capacity. Size is the number of actual elements. Capacity is the number of elements you have room to store. size must always be less than or equal to capacity. It's usually less than capacity.

So you need a capacity variable and a size variable. "Resizing" involves changing the capacity, not the size. Obviously, "resize" can lead to confusion. Feel free to call your variables arrayCapacity and numStudents , so it will be extra clear. I would in fact advise doing that to avoid confusion.

So any display must involve the "size" variable. Any "resizing" involves changing the "capacity" of the array.

VernonDozier
Posting Expert
5,527 posts since Jan 2008
Reputation Points: 2,633
Solved Threads: 711
 
Give this a try.

Thanks! This is much more efficient and gets exactly at what I'm trying to do.Don't confuse size with capacity. Size is the number of actual elements. Capacity is the number of elements you have room to store. size must always be less than or equal to capacity. It's usually less than capacity.

So you need a capacity variable and a size variable. "Resizing" involves changing the capacity, not the size. Obviously, "resize" can lead to confusion. Feel free to call your variables arrayCapacity and numStudents , so it will be extra clear. I would in fact advise doing that to avoid confusion.

So any display must involve the "size" variable. Any "resizing" involves changing the "capacity" of the array.

Alright I got it. Thanks again.


I should be set for now. I'm going to work on implementing maybe a selection sort to output the data in alphabetical order. Thanks again guys.

thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

I'm trying to add a recursive quicksort function to display the names in alphabetical order. In the code I'm only trying send the last names for each entry to be compared. Is this the right idea? Does anything stick out that needs to be fixed?

#include <fstream>
#include <iostream>
#include <cstring>

using namespace std;

struct Student
{
    char First[30];
    char Last[30];
    float Score1, Score2, Score3, Score4, Score5, Score6, Score7;

};

void Sort(char LastName[], int Length); 
 
void QuickSort(char LastName[], int Left, int Right);

int main()
{

ifstream infile;
ofstream outfile;
int numstudents = 1;
    
infile.open("input.txt");
    
struct Student * data;
struct Student * temp;
data = new struct Student[numstudents];


int i = 0;
while(!infile.eof())
{
	infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;
        
	if(!infile.eof())
	{
		numstudents++, i++;
		temp = new struct Student[numstudents];
		for( int c = 0; c < numstudents; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}
     infile.close();



    infile.close();
    outfile.open("output.txt");
    
    
    Sort(data[i].Last, numstudents);

    
    float score;
    for (int i = 0; i < numstudents - 1; i++ )
    {
            outfile << data[i].Last << ", " << data[i].First << "\t\t\t\t\t";
            
            score = (data[i].Score1 + data[i].Score2 + data[i].Score3 +
            data[i].Score4 + data[i].Score5 + data[i].Score6 +
            data[i].Score7) / 7;

            outfile << score << "\t\t\t\t";

            if (score >= 90)
            {
                outfile << 'A' << endl;
            }
            else if (score >= 80)
            {
                outfile << 'B' << endl;
            }
            else if (score >= 70)
            {
                outfile << 'C' << endl;
            }
            else if (score >= 60)
            {
                outfile << 'D' << endl;
            }
            else
            {
                outfile << 'F' << endl;
            }

           

    }

    delete [] data;
    delete [] temp;
    outfile.close();
     
    return 0;
}

void Sort(char LastName[], int Length) 
{ 
  QuickSort(LastName, 0, Length-1); 
} 
 
void QuickSort(char LastName[], int Left, int Right)  
{  
  int i, j;  
  char x, y;  
  
  i = Left; j = Right;  
  x = LastName[( Left + Right) / 2 ];  
  
  do {  
    while((LastName[i] < x) && (i < Right)) 
       i++;  
    while((x < LastName[j]) && (j > Left)) 
       j--;  
  
    if(i <= j) {  
      y = LastName[i];  
      LastName[i] = LastName[j];  
      LastName[j] = y;  
      i++; j--;  
    }  
  } while(i <= j);  
  
  if(Left < j) 
     QuickSort(LastName, Left, j);  
  if(i < Right) 
     QuickSort(LastName, i, Right);  
}
thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

You have written

infile.close();

two times.

At the top of the outfile.open() section you have

Sort(data[i].Last, numstudents);

I think you are thinking of putting that into a for() loop or something to call that function once per person in the list.

If you are going to do a self made sort then I would send the whole list into 1 function.

example

void Sort(Student data[], int size)
{
	for( int c = 0; c < size; c++ )
	{	
		int index = c;
		for( int i = c; i < size; i++ )
		{
			if( data[index].Last[0] > data[i].Last[0] )
			{
				index = i;
			}
			if( index != c && i == size-1 )
			{
				Student temp;
				temp = data[index];
				data[index] = data[c];
				data[c] = temp;
			}				
		}
	}
}

This sorts the last name by the 1st letter not based off the whole last name.

sfuo
Practically a Master Poster
656 posts since Jul 2009
Reputation Points: 164
Solved Threads: 99
 

The potential problem here is that they won't be in complete order since the list is 250+ entries. Are there any modifications to your code I can make to take the entire name into account? Also, for some reason the last entry is being cut out of the output file. Any clear reason for this? Thanks again, you've been an excellent help.

thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

I'll check about the modification but the reason why the last guy gets cut off is because you have numstudents-1 in the for() loop for output.

sfuo
Practically a Master Poster
656 posts since Jul 2009
Reputation Points: 164
Solved Threads: 99
 

Don't use the return value of eof() in controlling the loop. Sooner or later that syntax will result in creating a second copy of the last entry in the file. I haven't found a very readable explanation of why, so I don't post references. I recommend doing something like this:

while(infile >> data[i].First)
{
   infile >>  data[i].Last >> data[i].Score1 >> data[i].Score2
           >> data[i].Score3 >> data[i].Score4 >> data[i].Score5
		>> data[i].Score6 >> data[i].Score7;

or this:

while(infile >> data[i].First >>  data[i].Last >> data[i].Score1 >> data[i].Score2 >> data[i].Score3 >> data[i].Score4 >> data[i].Score >> data[i].Score6 >> data[i].Score7)
{

and someday you'll be able to make it even more readable by overloading the >> operator for the struct type you declared so you can write this:

while(infile >> data[i])
{


sfuo is probably correct about why the last one is cut out though.

What happened to doubling the capacity of data each time you reached maximum capacity instead of doing what you're doing? The current syntax you are using creates significant memory leak since every time you assign temp to data you loose the ability to access the memory that data was pointing to.

Since the name fields in the struct are C style strings you will need to compare them with the strcmp(), or similar, function in order to compare two names for lexical (in)equality. I believe strcmp() and related functions are in the cstring header file. You could use the equals operator to compare name fields if they were declared as STL string objects instead of C style strings.

Lerner
Nearly a Posting Maven
2,382 posts since Jul 2005
Reputation Points: 739
Solved Threads: 396
 

Yeah I haven't played around with the cstring lib and I use strings instead of char arrays so I read what Lerner said below and changed the sort() function.

void Sort(Student data[], int size)
{
	for( int c = 0; c < size; c++ )
	{
		int index = c;
		for( int i = c; i < size; i++ )
		{
			if( strcmp(data[index].Last, data[i].Last) > 0 )
			{
				index = i;
			}
			
			if( index != c && i == size-1 )
			{
				Student temp;
				temp = data[index];
				data[index] = data[c];
				data[c] = temp;
			}			
		}
	}
}

This works for what I've tested.
Don't forget to #include

And I changed the infile while() loop to what Lerner suggested too

while( infile >> data[i].First >> data[i].Last >> data[i].Score1 >> data[i].Score2
		>> data[i].Score3 >> data[i].Score4 >> data[i].Score5
    		>> data[i].Score6 >> data[i].Score7)
{
	if( !infile.eof() )
	{
		size++, i++;
		temp = new struct Student[size];
		for( int c = 0; c < size; c++ )
		{
			temp[c] = data[c];
		}
		data = temp;
	}
}
sfuo
Practically a Master Poster
656 posts since Jul 2009
Reputation Points: 164
Solved Threads: 99
 

Thanks guys! The program runs great. Your help is very much appreciated.

thisisanfield.1
Newbie Poster
11 posts since Sep 2009
Reputation Points: 4
Solved Threads: 0
 

This article has been dead for over three months

Post: Markdown Syntax: Formatting Help
You