C++ struct pointer and memory leak - c++

I am doing a c++ book question and got stucked on this specific parts where it says
"write program that asks how many score there are and how many student there are. it should then dynamically allocate an array of structures, each structure's Test member should point to a dynamically allocated array that will hold the test scores,after array has been dynamically allocated the program should ask for the id number and test score for each student".
right now I have a problem in the for loop where there is a memory leak and program crashes after input values, any suggestions ?
here is the code:
struct Course
{
string Name;
int IdNumber;
int *Tests;
int Average;
int courseGrade;
};
void Result(int,int );
int main()
{
cout<<"please enter number of test score ";
int testScore;
cin>>testScore;
int numberofStudents;
cout<<"please enter number of student there is ";
cin>>numberofStudents;
Result(numberofStudents,testScore);
}
void Result(int numberofStudents,int testScore)
{
const int Size=numberofStudents;
const int Size1=testScore;
Course *object=nullptr;
object=new Course[numberofStudents];
object->Tests = new int[testScore];
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
}
here is the input on the console
please enter number of the test scores :3
please enter number of students there is :3
34
90
the program crashes after I input 90

Here's what I suspect is happening:
In this for loop:
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
You access object using testScore as an index. If testScore is larger than the length of object you will run into issues.
The memory leak problem comes from the fact that you are allocating space for object and every Tests member of a Course but you never free that memory.

There is a memory leak because you are allocating things withj new and never freeing them with delete.
As for the crash, it's being caused by this line:
object->Tests = new int[testScore];
Remember that object isn't a Course object, it's an array of Course objects, every one of which needs it's own Tests array. The line is effectively just object[0].Tests = ...
So You need a loop on numberofStudentsto allocate the test in each Course, and the another loop on numberofStudents around the loop on testScore (which you already have) to gather all the grades. (Advanced study: you can combine those two loops)

I have made some changes in your code, and it doesn't crash anymore. Why you need to use pointer of Test. I think it is good to use static memory instead of dynamic memory. Please check this
#include<iostream>
#include<cstring>
using namespace std;
struct Course
{
string Name;
int IdNumber;
int Tests[100];
int Average;
int courseGrade;
};
void Result(int,int );
int main()
{
cout<<"please enter number of test score ";
int testScore;
cin>>testScore;
int numberofStudents;
cout<<"please enter number of student there is ";
cin>>numberofStudents;
Result(numberofStudents,testScore);
}
void Result(int numberofStudents,int testScore)
{
const int Size=numberofStudents;
const int Size1=testScore;
Course *object=nullptr;
object=new Course[numberofStudents];
//object->Tests = new int[testScore];
for(int i=0;i<testScore;i++)
{
cin>>object[i].Tests[i];
}
}

Related

Nested structure function in c++

I'm having trouble with one of my assignments (or maybe I'm overthinking it?)
I need to create
a function to take integer parameters for number of students and tests.
Allocate the memory needed for the array of students and the array of test scores for each student.
Return a pointer to the array of Student structures. No display output is done in this function.
int main()
{
int studentNum;
int testNum;
cout << "How many students are there?\n";
cin >> studentNum;
cout << "How many tests are there?";
cin >> testNum;
system("pause");
return 0;
}
my function
GradeBook *initArrays(int s, int t)
{
GradeBook *ptr;
// Allocate the array of Student structures.
ptr = new GradeBook[s];
// Allocate an array of ints (to hold test scores)
// for each element of the array of Student structures.
for (int count = 0; count < s; count++)
{
ptr[count].tests = new int[t];
}
// Return a pointer to the array of structures.
return ptr;
}
edit: I have edited my function, could i get some opinions on that?
if you are writing this in c++, use classes. if i understand correctly, you should create a structure to save a students id,name,or something and a corresponding grade?
something like:
class Test{
public:
int id;
int grade;
Test(int id, int grade){
this->id = id;
this->grade = grade;
}
};
class Student{
public:
int id;
std::string name;
std::vector<Test> tests;
Student(int id, std::string name)
{
this->id = id;
this->name = name;
}
};
int main(){
vector<Student> students;
int studentnum;
for (int i = 0; i < studentnum; i++){
students.push_back(Student(i, "name"));
//insert all tests of the student by calling students[i].tests.push_back(Test(id, grade))
}
}
this way you don't have to allocate memory, which you can easily overlook freeing.
edit:
this is very basic and not a sophisticated solution, as the properties of the classes are all public.
edit 2:
typedef struct Test{
int id;
int grade;
}Test;
typedef struct Student{
int id;
std::string name;
Test * tests;
}Student;
int main(){
Student * students;
int studentnum;
students = (Student*)malloc(sizeof(Student)*studentnum);
for (int i = 0; i < studentnum; i++){
students[i]->id = id;
students[i]->name = "name";
student[i]->tests = (Test*)malloc(sizeof(Test)*numberofgradesofthatstudent);
for (int j = 0; j < numberofgradesofthatstudent; j++)
{
students[i]->tests[j]->id = testid;
students[i]->tests[j]->grade = grade;
}
}
}
this is schematic! new and malloc reserve memory on the heap, do not forget to free everything when you are done with it.
As said a little above, be careful using brackets {} to delimit your blocks.
Secondly,the syntax:
array[studIndex].Tests
supposes that the value array[studIndex] (here an integer) has a member value named Tests. But in this case it doesn't.
Think about your problem: you need to store two values "connected" to one another in a static array. The way I see it, you should try on with two dimensional arrays:
int 2dArray[nbStudents][nbTests];
If you don't want to bother with 2dimensional arrays, you can also try
int 2dArray[nbStudents * nbTests];
But for conveniance, it is often better to use 2d arrays.
Also, think about declaring your array before the for loops in your function.
Then concatenate two for loops as you did and I'll let you think about the rest...

C++: Accessing a structure array's element crashes the program

I'm trying to learn structures and I have a program which crashes for me - can't understand why. The compiler throws out a bad access exception when trying to access an element of an array of structures.
Here's the code:
#include <iostream>
#include <fstream>
FILE *input;
int n=1;
struct Student {
string name;
string city;
int grades[4];
};
Student *students = new Student[n];
int main(int argc, const char * argv[]) {
input = fopen("input.txt", "r");
if(input==NULL) {
cout<<"\nCan't open file input.txt";
return;
}
int size;
if(fscanf(input, "Size%d\n\n",&size)<=0) {
cout<<"\nAn error occurred.";
return;
}
n=size;
cout<<"\nThe size is "<<n;
for(int i=0;i<n-1;i++) {
Student newStud;
char name[255];
char city[255];
fscanf(input, "\n%s\n%s\n%d;%d;%d;%d;",name,city,&newStud.grades[0],&newStud.grades[1],&newStud.grades[2],&newStud.grades[3]);
newStud.fullName = name;
newStud.city = city;
cout<<"\nAdding student at "<<i;
students[i]=newStud;
cout<<"\nAdded successfully";
}
fclose(input);
cout<<"\nLoad complete!";
}
input.txt:
Size5
Bob
NY
10;10;10;10;
Rick
SF
6;6;6;6;
John
NY
2;3;2;5;
Jack
NY
5;5;5;4;
Console output:
The size is 5
Adding student at 0
Added successfully
Adding student at 1
The initialization of the students pointer is done before the execution of main. You wouldn't have read n by then. Therefore, your code ends up allocating memory for 1 Student in students array.
The code in your main assumes that the students array is capable of holding size (or the current value of n) while it isn't really the case. Hence, the code ends up accessing unknown locations which causes undefined behavior (a segmentation fault almost always).
The fix is to allocate memory for the array after the input for n has been taken.
Obviously,the program crashed when you tried to access the second Student in the array while you only allocated one size of the Student for it. You should allocate the array’s memory after the code “n = size” but not before,which you did at the top of the main entrance is absolutely unreasonable for your purpose.

Segmentation fault with dynamically allocated arrays as members of structure in c++?

I am writing a program using an array of structures to store a name, id number, and an array of test scores for a certain amount of students. Both the array of structures and the array of test scores member need to be dynamically allocated. I've gotten down to the function that allows the user to input test scores for each student, however I am having problems with the cin in the last function (getScores function). When using Linux I get a segmentation fault, so I'm assuming it has something to do with the dynamically allocated tests array that is a member of the structure, I just can't see it. I'm wondering how I can go about debugging it and an explanation of why this is actually occurring so I can avoid it in the future.
//Preprocessor Directives
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
//Structure declaration
struct Students
{
string name; //Student name
int idNum; //Student ID number
double *tests; //Pointer to an array of test scores
};
//Function prototypes
Students *getStudents(int &);
double *getTests(int &);
void getInfo(string &, int &, int);
void getScores(double &, string, int);
//Main program section
int main ()
{
int numStudents = 0;
int numTests = 0;
Students *studentFiles = NULL;
//Call the getStudents function
studentFiles = getStudents(numStudents);
//Call the getTests function
studentFiles->tests = getTests(numTests);
for(int i = 0; i < numStudents; i++)
{
//Call the getInfo function
getInfo(studentFiles[i].name, studentFiles[i].idNum, i+1);
}
for(int i = 0; i < numStudents; i++)
{
for(int j = 0; j < numTests; j++)
{
getScores(studentFiles[i].tests[j], studentFiles[i].name, j);
}
}
delete [] studentFiles;
delete [] studentFiels->tests;
return 0;
}
Students *getStudents(int &numStudents)
{
Students *studentFiles = NULL;
//Prompt the user for the number of students
cout<<"Enter the number of students: ";
cin>>numStudents;
//Dynamically allocate an array of structs, one for each student
studentFiles = new Students[numStudents];
return studentFiles;
}
double *getTests(int &numTests)
{
double *tests = NULL;
//Prompt the user for the number of tests
cout<<"Enter the number of tests: ";
cin>>numTests;
cin.ignore();
//Dynamicall allocate an array of integers, one for each test
tests = new double[numTests];
return tests;
}
void getInfo(string &name, int &idNum, int index)
{
//Prompt for each student's name and id number
cout<<"Enter the name of student #"<<index<<": ";
getline(cin, name);
cout<<"Enter the id number of student #"<<index<<": ";
cin>>idNum;
cin.ignore();
}
void getScores(double &test, string name, int numTest)
{
cout<<name<<endl;
cout<<numTest<<endl;
//Prompt for each test score for each student
cout<<"Enter "<<name<<"'s score for test #"<<numTest+1<<": ";
cin>>test;
}
One error is that you access a member of deleted object studentFiles. Reverse the lines to fix that:
delete [] studentFiles->tests;
delete [] studentFiles;
Ideally, use std::vector<> instead of dynamically allocating and releasing memory manually.
Also note, that the code only initializes Student::tests of the first member of the array, the rest of Student objects have this member uninitialized. The result of expression studentFiles[i].tests[j] is undefined and is likely to cause a crash.
You need to initialize Student::tests member of each Student. And when done, deallocate Student::tests of each Student.

Store intime/outtime according to roll no.s in a file

I have been asked to write a program that asks the rollno from user to let the student go inside first and then keep that InTime fixed and only change the outTime by asking the rollno of the student within a given range any number of times the user wishes to send them out. Here is the code i wrote:
#include<iostream.h>
#include<fstream.h>
#include<time.h>
int main()
{
char *intime[10],*outtime[10],d='\0';
int rollno[10]={10,4,1,5,6,2,3,7,9,8},i,c;
time_t obj;
time(&obj);
struct tm* t;
t=localtime(&obj);
ofstream f;
f.open("fit.txt");
cout<<"Enter your roll no to go in.\n";
cin>>c;
intime[c]=asctime(t);
cout<<"Enter your roll no to go out.\n";
cin>>c;
while(c>=1 && c<=10)
{
outtime[c]=asctime(t);
}
f<<rollno[c]<<" "<<intime[c]<<" "<<outtime[c]<<endl;
f.close();
ifstream t1;
t1.open("fit.txt");
for(i=0;i<10;i++)
{
cout<<" "<<rollno[i]<<" "<<intime[i]<<" "<<outtime[i]<<endl; }
t1.close();
return 0;
}
Problem is that since i am unable to update intime and outtime apart from those that are requested for, iam getting garbage value.. help needed.
Update: here is the final code that i have come up with ,it's not giving any garbage values and is giving different values of intime and outtime, please feel free to suggest further improvements on this
#include<iostream.h>
#include<fstream.h>
#include<time.h>
int main()
{
int i,j;char *intime[10],*outtime[10],it[10],ot[10];
for(i =1;i<=10;i++)
{
intime[i]="0";
outtime[i]="0";
}
int rollno[10]={10,4,1,5,6,2,3,7,9,8},c;
time_t obj,obj1;
time(&obj);
struct tm* t;
struct tm* u;
t=gmtime(&obj);
ofstream f;
f.open("fit.txt");
cout<<"Enter your roll no to go in.\n";
cin>>c;
for(i=1;i<=10;i++)
if(rollno[i]==c)
{strftime(it,10,"%I:%M:%S",t);
intime[i]=it;}
cout<<"Enter your roll no to go out.\n";
cin>>c;
time(&obj);
u=gmtime(&obj);
for(j=1;j<=10;j++)
if(rollno[j]==c)
{
if(intime[j]!="0")
{ strftime(ot,10,"%I:%M:%S",u);
outtime[j]=ot;}
}
f.close();
ifstream t1;
t1.open("fit.txt");
for(i=1;i<=10;i++)
cout<<" "<<rollno[i]<<" "<<intime[i]<<" "<<outtime[i]<<endl;
t1.close();
return 0;
}
You need to initialize the intime and outime arrays right after you create them. Somethine like
std::memset(intime, 0, sizeof(intime));
Should work for you.
You also have a problem with your use of the asctime function. It returns the address of a static string which will get overwritten after each call to asctime. You'll need to allocate storage yourself and copy the returned string.

Adding element to array of struct c++

Can someone explain why this code does not work? It keeps crashing when it asks for input in addCar().
I think something is wrong with copying an array, but I can't figure out what exactly. I also tried to use copy() but it didn't work either.
#include <iostream>
#include <string>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct Car{
string Brand;
string model;
long mileage;
};
void addCar(int *ptr, struct Car *arra){
*ptr=*ptr+1;
Car *newArr = new Car[*ptr];
memcpy(newArr, arra, (*ptr)*sizeof(Car));
cout<<"Brand ";
getline(cin,newArr[*ptr].Brand);
cout<<"Model ";
getline(cin, newArr[*ptr].model);
cout<<"mileage ";
cin>>newArr[*ptr].mileage;
arra=newArr;
};
int main(int argc, char** argv) {
int size=1;
int *ptr_size;
ptr_size=&size;
Car *tab=new Car[*ptr_size];
tab[0].Brand = "Audi";
tab[0].model = "A8";
tab[0].mileage = 14366;
addCar(*ptr_size, tab);
return 0;
}
The fail is probably here:
getline(cin,newArr[*ptr].Brand);
A bit above, you did this: *ptr=*ptr+1; and made newArr an array of *ptr elements. Arrays are origin zero. That means the first item in the array is newArr[0]. The last will be at newArr[*ptr-1], so writing into newArr[*ptr] is writing over someone else's memory. Generally a bad thing to do.
But this is also not cool:
*ptr=*ptr+1;
Car *newArr = new Car[size+1];
memcpy(newArr, arra, (*ptr)*sizeof(Car));
You increment the size of the array. That's OK.
You create a new array with the new size. That's OK.
You copy new size number of elements from the old array to the new array and over shoot the end of the old array. Not OK.
The best answer is given by Jerry Coffin and Paul McKenzie in the comments: use a std::vector. If this is not allowed... Ick.
But alrighty then.
First, memcpy literally copies a block of memory. It does not know or care what that block of memory is or what it contains. Never use memcpy unless you are copying something really, really simple like basic data type or a structure made up of nothing but basic data types. String is not basic. The data represented by a string might not be inside the string. In that case, you copy a pointer to the string and that pointer will not be valid after the death of the string. That's not a problem in your case because you don't kill the string. That leads to problem 2. Let's fix that before you get there. The easiest way (other than vector) is going to be:
for (int index = 0; index < *ptr-1; index++)
{
newArr[index] = arra[index];
}
An optimization note. You don't want to resize and copy the array every time you add to it. Consider having two integers, one size of array and the other index into array and double the size of the array every time the index is about to catch up with the size.
When you allocate any memory for data with new somebody has to clean up and put that memory back with delete. In C++ that somebody is you. so, before you arra=newArr; you need to delete[] arra;
Passing in the array index as a pointer overcomplicates. Use a reference or just pass by value and return the new index. Also, don't name a variable ptr. Use something descriptive.
void addCar(int &arrasize, struct Car *arra){
or
int addCar(int arrasize, struct Car *arra){
Next problem: int addCar(int arrasize, struct Car *arra){ passes in a pointer to arra. But you passed the pointer by value, made a copy of the pointer, so when you change the pointer inside the function, it's only the copy that got changed and the new array is not going to come back out again. So,
int addCar(int arrasize, struct Car * & arra){
Passes in a reference to the pointer and allows you to modify the pointer inside the function.
Putting all that together:
int addCar(int size, struct Car * & arra)
{
Car *newArr = new Car[size + 1];
for (int index = 0; index < size; index++)
{
newArr[index] = arra[index];
}
cout << "Brand ";
getline(cin, newArr[size].Brand);
cout << "Model ";
getline(cin, newArr[size].model);
cout << "mileage ";
cin >> newArr[size].mileage;
delete[] arra;
arra = newArr;
return size+1;
}
int main()
{
int size=1;
Car *tab=new Car[size];
tab[0].Brand = "Audi";
tab[0].model = "A8";
tab[0].mileage = 14366;
size = addCar(size, tab);
// do more stuff;
// bit of test code here
for (int index = 0; index < size; index++)
{
cout << "Car " << index << " brand =" <<tab[index].Brand << " Model=" << tab[index].model << " mileage=" <<tab[index].mileage << endl;
}
delete[] tab;
return 0;
}
When you are copying the old array to the new one you are accessing invalid memory, remember that, in that point, arra has size *ptr-1 not *ptr, so the line should be
memcpy(newArr, arra, (*ptr-1)*sizeof(Car));
also in the other lines you should insert the new value in the *ptr-1 position because the indexes in newArr go from 0 to size-1 ie *ptr-1:
cout<<"Brand ";
getline(cin,newArr[*ptr-1].Brand);
cout<<"Model ";
getline(cin, newArr[*ptr-1].model);
cout<<"mileage ";
cin>>newArr[*ptr-1].mileage;