Nested structure function in c++ - 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...

Related

Code exiting when Dynamic Array of class allocated

I am trying to dynamically allocate an array and whenever it gets to the part where it dynamically allocates the program exits. I would rather not use vectors as I am trying to learn how to do this using dynamic arrays.
This is the simplified code:
#include <iostream>
#include <string>
using namespace std;
class Student
{
private:
double calcAverage(double* testArray);
char calcGrade(double average);
public:
int nTests, sameTests, idNum;
string name;
double average, *testArray;
char grade;
};
int i;
Student fillStudentArray(int nStudents);
int main()
{
*studentArray = fillStudentArray(nStudents);
return 0;
}
Student fillStudentArray(int nStudents)
{
Student *newStudentArray = new Student[nStudents];
cout << "If you can see this I worked. ";
delete[] studentArray;
return *newStudentArray;
}
I have tried the solution posted here Creation of Dynamic Array of Dynamic Objects in C++ but it also exits in a similar way. The main for the code looks like this.
int main()
{
int nStudents = 3; //this number is just for testing, in actual code it has user input
Student** studentArray = new Student*[nStudents];
cout << "1 ";
for(i = 0; i < nStudents; i++)
{
cout << "2 ";
studentArray[i] = new Student[25];
cout << "3 ";
}
return 0;
}
close (heres a cigar anyway)
Student* fillStudentArray(int nStudents); <<== function must return pointer to students
int main()
{
int nStudents = 3; <<<=== declared nstudents
Student *studentArray = fillStudentArray(nStudents); <<< declare studentArray
return 0;
}
Student *fillStudentArray(int nStudents) <<<== return pointer
{
Student* newStudentArray = new Student[nStudents];
cout << "If you can see this I worked. ";
// delete[] studentArray; <<<== what were you trying to delete?
return newStudentArray; <<<=== return pointer
}
the second code you showed is not relevant, its creating a 2d array

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.

New to C++, program not responding when dereferencing part of a vector?

I learned C++ yesterday an I'm trying to solve USACO training problems.
http://train.usaco.org/usacoprob2?a=iKSzALidh4Q&S=gift1
For this one, I have created a vector of People pointers. However, after some troubleshooting, I discovered that when I try to do something like
Person bob = *(people.at(i));
or
people.at(i) -> setbalance(giveself); // giveself is an int
The program is not responding and:
Process terminated with status -1073741819 (0 minute(s), 3 second(s).
I'm also new to this forum.
Here is my code:
include statments
using namespace std;
class Person
{
private:
int balance;
int origbalance;
string name;
public:
int getbalance() {return balance;}
string getname() {return name;}
void setbalance(int b){balance +=b;}
void setorigbalance(int o) {origbalance = o;}
int getorigbalance() {return origbalance;}
void giveTo(int num, Person* y) {y->setbalance(num);}
~Person();
Person(string n);
};
Person::Person(string n)
{
name = n;
}
Person::~Person()
{
}
int main()
{
ofstream fout ("gift1.out");
ifstream fin ("gift1.in");
int NP;
fin>>NP;
cout<<NP<<endl;
vector<Person*> people(NP);
cout<<"Created vector\n"<<endl;
for(int i = 0; i<NP; i++)
{
string nam;
fin>>nam;
Person* p = new Person(nam);
people.push_back(p);
cout<<nam<<endl;
}
cout<<"\nFilled vector, size = "<<people.size()<<endl;
for(int i = 0; i<NP; i++)
{
string temp;
fin>>temp;
cout<<"\nNow receiving "<<temp<<endl;
int togive, numgiving;
fin>>togive>>numgiving;
cout<<"\n"<<temp<<" is dividing "<<togive<<" among "<<numgiving<<" people"<<endl;
Person bob = *(people.at(i));
cout<<"hi bob"<<endl;
//(*people.at(i)).setorigbalance(togive);
cout<<"Original balance set"<<endl;
int giveeach = togive/numgiving;
cout<<"or "<<giveeach<<" to each person"<<endl;
int giveself = togive%numgiving;
cout<<"and "<<giveself<<" to himself :/"<<endl;
people.at(i) -> setbalance(giveself);
for(int j=0; j<numgiving; j++)
{
string nametogiveto;
fin>> nametogiveto;
cout<<nametogiveto<<endl;
for(int k=0; k<NP; k++)
{
string namy = people.at(k)->getname();
if(namy==nametogiveto)
{
cout<<"\nHere you go "<<namy<<" have "<<giveeach<<endl;
people.at(k)->setbalance(giveeach);
people.at(i)->setbalance(-giveeach);
break;
}
}
}
}
for(int i=0; i<NP; i++)
{
cout<<people.at(i)->getname()<<endl;;
cout<<people.at(i)->getorigbalance() - people.at(i)->getbalance()<<endl;
cout<<endl;
fout<<people.at(i)->getname();
fout<<people.at(i)->getorigbalance() - people.at(i)->getbalance()<<endl;
}
return 0;
}
You're running into undefined behavior due to dereferencing null pointers.
vector<Person*> people(NP);
This line creates the vector with NP null pointers. You later add on your actual pointers but only ever access the first NP elements which are the null pointers.
That said you don't even need pointers here and I'd recommend getting rid of them. In fact you have memory leaks due to allocating pointers with new but never calling delete on them. In my experience it's typical for people just starting out with C++ to overuse pointers, so think about ways to avoid them first.
Change
vector<Person*> people(NP);
to
vector<Person> people;
and fill it using:
for(int i = 0; i<NP; i++)
{
string nam;
fin>>nam;
Person p(nam); // no more need for pointer or new here
people.push_back(p);
cout<<nam<<endl;
}
later when accessing it you don't need any dereferencing anymore either. That means you can get rid of all the * for example:
Person bob = *(people.at(i));
turns into:
Person bob = people.at(i);
and you can access member functions with . instead of -> everywhere, for example:
people.at(k)->setbalance(giveeach);
would turn into:
people.at(k).setbalance(giveeach);
This means getting rid of a lot of unneeded dereferencing of pointers and also of the memory leak you previously would have had.
For me, it seems that you are getting that error since opening input file fin is not successful. To check it, add the following line after you define fin variable:
ifstream fin ("gift1.in");
if(!fin) {
cout << "Error opening input file.\n";
}
In any case, it's always a good practice to check if the file is opened successfully.

String in struct in struct in C++

So I've to do another exercise. This time I need to define a struct and a 100-elements array, which will store information about the book (title, author, ID number, price) and a simple function which will print info about all of the books stored. I started with that code:
#include <iostream>
using namespace std;
int main()
{
struct name_surname {string name, surname;};
struct book {string title; name_surname author_name, author_surname; int ID; int price;};
return 0;
}
And, well, what now? How can I store this in an array?
You just create an array of type book or name_surname or whatever you want.
Example:
book arr[100];
arr[0].title = "The last robot";
arr[0].ID = 2753;
Tips:
It's good programming practice if your structs/classes begin with with capital letter, so it's easier to distinguish them and so it is easier to name the variable the same name just without the capital letter. Example.
struct Name_surname
{
string name, surname;
};
Name_surname name_surname[100];
name_surname[0].name = "MyName";
Another tip is that I'd really suggest you learn how to research, this question has been answered millions of times and answers are all over the internet.
Here is my suggestion :
struct book
{
string title;
string name_surname;
string author_name;
string author_surname;
int ID;
int price;
};
struct Database
{
book *array;
void printDatabase()
{
for(int i = 0 ; i < 100 ;i++)
cout<<array[i].title<<endl;
}
Database()
{
array = new string [100];
}
};
Your name structure seems a little confused but creating an array is simply a case of declaring a variable with [] appended to it giving the size.
For example:
struct full_name
{
std::string firstname;
std::string surname;
};
struct book
{
std::string title;
full_name author;
int ID;
int price;
};
int main()
{
// Declare an array using []
book books[100]; // 100 book objects
// access elements of the array using [n]
// where n = 0 - 99
books[0].ID = 1;
books[0].title = "Learn To Program In 21 years";
books[0].author.firstname = "Idont";
books[0].author.surname = "Getoutalot";
}
What do you think about that:
#include <iostream>
using namespace std;
struct book {string title; string name; int ID; int price;} tab[100];
void input(book[]);
void print(book[]);
int main()
{
input(tab);
print (tab);
return 0;
}
void input(book tab[])
{
for (int i=0;i<3;i++)
{
cout<<"\nBook number: "<<i+1<<endl;
cout<<"title: ";cin>>tab[i].title;
cout<<"name: ";cin>>tab[i].name;
cout<<"ID: ";cin>>tab[i].ID;
cout<<"price: ";cin>>tab[i].price;
}
}
void print (book tab[])
{
for (int i=0; i<3; i++)
{
cout<<"\nBook number: "<<i+1<<endl;
cout<<"title: "<<tab[i].title;
cout<<"\nname: "<<tab[i].name;
cout<<"\nID: "<<tab[i].ID;
cout<<"\nprice: \n"<<tab[i].price;
}
}
I've done this with help from some Yt video. It works, but, is there a way to do it better, or just leave it how it is? And I have a question: Why those function parameters? Can't I just say tab[] or something else?
Computer languages are based on general and recursive rules. Just try to experiment and extrapolate with the basic understanding to build seemingly complex stuff. Coming to what you are trying to achieve:
We know, an array can be declared for any data-type (primitive or derived, one might call them POD and ADT).
We know, struct can comprise of any number of elements of any data-types.
Now, we can see that it is just as natural to say MyStruct[] as it is to int[].
It is better to prefer std::array if using modern compiler.

c++ dynamic classes without using vector

So I happened to finish my homework program but today during lecture my good ol professor told us we are not allowed to use STL as in vectors or list to create our database. We were also told we need all our variables private. I was doing this program completely wrong. This is what I have so far.
in class.h
class Student {
private:
string last;
string first;
int student_id;
int enroll_id;
int *grades;
}
class Gradebook {
public:
Gradebook();
void newCourse(Gradebook *info);
private:
string name;
int course_id;
int count_course;
int enroll;
Student *students;
public:
//Constructor
}
I know I can access private members of Gradebook by using a constructor so I can set every member in my Gradebook.
function to create a newCourse
Gradebook::Gradebook() {
students = new Student;
course_id=0;
count_course=0;
enroll = 0;
}
Gradebook::newCourse(Gradebook *info) {
int i, loop=0;
cout << "Enter Number of Courses: ";
cin >> loop;
info = new Gradebook[loop];
for(i=0; i<loop; i++) {
cout << "Enter Course ID: ";
cin >> info[info->count_course].course_id;
cout << "Enter Course Name: ";
cin >> info[info->count_course].name;
info->count_course++
}
}
So Courses are now sets. Since the variables in Student are private. I can't just use a pointer to the variables to access them. Can someone show me how to do this?
Ok I didnt know how to ask this question but I actually answered it. But I want everyones opinion on my method.
class Student {
public:
void setID(int ID){student_id = ID; };
int getID(){return student_id);
private:
string last;
string first;
int student_id;
int enroll_id;
int *grades;
};
class Gradebook {
public:
Gradebook();
void newCourse(Gradebook *info);
void newStudent(Gradebook *info);
private:
string name;
int course_id;
int count_course;
int count_student;
int enroll;
Student *students;
public:
//Constructor
}
void Gradebook::newStudent() {
int i, loop=0;
int student=0;
string lastName;
cout << "Enter number of students: ";
cin >> loop;
for(i=0; i<loop; i++) {
cout << "Enter Student ID: ";
cin >> student;
info->students[info->count_student].setID(student);
cout << "Enter Last Name: ";
cin >> lastName;
info->students[info->count_student].setLast(lastName);
info->count_student++;
}
}
Is there anything wrong of doing it this way?
edit: You can't use 'Student *students' for your container for multiple Students...
You could use your own lists. Something like
struct Participant{ // or class (and maybe more clever name)
Student *student;
Participant *prev;
Participant *next;
};
You have to do little pointer-acrobatics, but maybe that's the idea for this exercise..
And like in previous answer, use get and set functions in your Student class.
Ok, I'm sorry but your code is a mess...
I can't do this homework for you, but here is few tips that came in mind
Are you sure that it wouldn't be easier to make different classes for Student, Course and Gradebook? I don't know your homework specifications, so I can't be sure what it is that your program should actually do.
You can not use int *grades to store all of one students grades. Same goes for Student *students. You can not access iterate *students like an array students[i].something()
If you use some help class(or struct) like Participant, you have to find right student by iterating in loop. Notice, that you have store 'first' participant inside your class and 'int students' keep in your student count (also inside your class)
Participant *current = first;
int id = Id; // Id is from function/method call
for(unsigned int i = 0; i < students; ++i){
if(current->getId()== id){
// do something and
break;
}
else if(current->next != NULL){ // test this just in case
current = current->next;
}
else break; // something went wrong
}
It might be good idea to store all students in one list (that you make) and use pointers in Course or in Gradebook or where ever... If you know 'static' here is a hint
class Student{
public:
. // get and set functions
static Student* getStudent(int id); // return NULL if no student with id
private:
static Student *first;
static Student *last;
static unsigned int counter; // helps with loops, but you can always use next != NULL
Student *next;
Student *prev;
}
Put this inside the Student constructor
with first student that you create you set first and last to point him/her, and next and prev to NULL. After that, always when you create a new student you set
this->prev = last;
last->next = this;
last = this;
this->next = NULL;
Do not implement program logic inside class. Do it in main() or in some other function, but i think main() is fine for now. If you need to add new student in gradebook, ask all the necessary info in main() and make some function like Gradebook::newStudent(int &Id, string &last_name){ // store student info in gradebook }
Usually these homework programming exercises don't have be too fancy or streamlined or fully optimized. So don't overdo them ;)
class Student {
private:
string last;
string first;
int student_id;
int enroll_id;
int *grades;
public:
string &getLast(){ return Last; }
...
...
};
Call Student::getLast() when you need to access your last variable etc
or
void setLast(string sLast){last = sLast;} for writing and
string getLast(){return last;} for reading
And example of dynamic array:
struct Student;
int Count = 0;
Student *Students = nullptr;
int AddStudent(Student nStudent)
{
Student *Buffer = new Student[Count + 1];
for (int a = 0; a < Count; a++)
Buffer[a] = Student[a];
Buffer[Count] = nStudent;
if(Students)
delete[] Students;
Students = Buffer;
return ++Count -1
}
void RemoveStudent(int Index)
{
Student *Buffer = new Student[Count - 1];
for (int a = 0; a < Index; a++)
Buffer[a] = Students[a];
for (int a = Index; Index < Count - 1; a++)
Buffer[a] = Students[a - 1];
if (Students)
delete[] Students;
Students = Buffer;
}