How to store same type structs in an array.

Pages: 12
Hello everyone, I need help for storing structs in an array. I don't have any constant values so, I get a syntax error. My program reads name, id number, 2 exam grades and calculates average grade of the student. Before sorting students according to their maximum grades, I need to store them in an array but student number is not constant. Any suggestions that I can use?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
///////// student header //////////
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
#include <iostream>
#endif

using namespace std;

struct student{
	string studentName;
	long studentID;
	int examGrade[2];
	float averageGrade;
};
////// cpp that contains main /////////
#include "student.h"
#include "studentutil.h"
#include "util.h"

using namespace std;

string nameCheck(){ /* Reads and checks a name if it is less or equal to 30 characters.*/
	string name;
	cin >> name;
	if (name.length() <= 30) return name;
	else nameCheck();
}

int numberOfStudents(){ // Reads and checks if the number is non-negative.
	int num;
	cout << "Please enter a non-negative integer:";
	cin >> num;
	if (num > 0) return num;
	else {
		cout << "You have entered a wrong value" << endl;
		 numberOfStudents();
	}
}


int main(){
	int numOfStudents = numberOfStudents();
	string name;
	int studentNum, grade1, grade2;
	
for (int counter= 0; counter < numOfStudents;counter++){
		cout << "Please enter student name:";
		name = nameCheck();

		student stu;
		stu.studentName = name;
		cout << "Please enter student number:";
		cin >> studentNum;
		stu.studentID = studentNum;

		cout << "Please enter student's 1st exam grade:";
		cin >> grade1;
		cout << "Please enter student's 2nd exam grade:";
		cin >> grade2;
		stu.examGrade[0] = grade1;
		stu.examGrade[1] = grade2;

		studentUtil stuUtil; 
		stu.averageGrade = (float)stuUtil.maxGrade(stu,2);/*int studentUtil::maxGrade(student student,
 int choice) if choice is 1 returns max of two grades, if 2 returns average grade.*/

		cout << stu.studentName << " " << stu.studentID << " " << stu.examGrade[0] << " " << stu.examGrade[1] << " " << stu.averageGrade << endl;
	}
	

	cin.get();
	cin.ignore();
	return 0;
}
Last edited on
I can't use new libraries in this program because it's a homework :/ But thank you anyway.
struct student STUDENTS[numOfStudents];

you will have an array of how ever many students you want.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct student { /* ... */ } ;

int numberOfStudents() ;

int main()
{
    const int MAX_STUDENTS = 1024 ; // the maximum possible number of students
    student students[MAX_STUDENTS] ; // define an array of size MAX_STUDENTS

    int numOfStudents = numberOfStudents(); // the actual numbrer of students
    if( numOfStudents > MAX_STUDENTS )numOfStudents = MAX_STUDENTS ;
    // use the first numOfStudents positions in the array

    for( int i = 0 ; i < numOfStudents ; ++i  )
    {
        student stu ;

        // get input and assign to stu

        // ...

        students[i] = stu ; // store the student at position i in the array
    }

    // at this point, students[0], students[1], ..., students[ numOfStudents-1 ]
    // contains the information for numOfStudents students
    // the rest of the array is unused
}

rafae11 -> When I do that it says Expression must have a constant value
Last edited on
JLBorges, thanks it is working, but I am curios about how I can write a code that is not predictable. We assumed the number is 1024 but what about when it is ambiguous ?
Last edited on
As a variation, you could use reference

1
2
3
4
5
6
7
8
    for( int i = 0 ; i < numOfStudents ; ++i  )
    {
        student& stu = students[i];

        // get input and assign to stu

        // ...
    }


Andy
As a variation, you could use reference

Andy thank you for your suggestion, but I couldn't understand the usage of reference here. Can you explain it? Thank you !
> how I can write a code that is not predictable. We assumed the number is 1024

Use std::vector<> http://www.mochima.com/tutorials/vectors.html
> how I can write a code that is not predictable. We assumed the number is 1024

Use std::vector<> http://www.mochima.com/tutorials/vectors.html


Ok, thank you for your help :)
Or if the use of vector is forbidden, as you suggested earlier, then consider new [] and delete [].
http://www.cplusplus.com/doc/tutorial/dynamic/
student& stu = students[i];

Here the compiler is using the name stu as an alias for students[i]. That is, the compiler know that the symbol stu means students[i]; there is no separate storage for it. So the compiler will generate identical code for

1
2
3
4
5
6
	student& stu = students[i];

	stu.studentName = name;
	stu.studentID = studentNum;
	stu.examGrade[0] = grade1;
	stu.examGrade[1] = grade2;


and

1
2
3
4
	students[i].studentName = name;
	students[i].studentID = studentNum;
	students[i].examGrade[0] = grade1;
	students[i].examGrade[1] = grade2;


Here it's not such a bid deal as students[i] isn't mush longer than stu (though I do tend to use a reference if working with more than a few struct member variables, as I think it looks tidier.)

When you working with nested structure, then it can be of much more use.

Andy

PS You mention that "can't use new libraries in this program because it's a homework". Does this also applies to classes? Because if you're allowed to use classes, then you could write your own little array class to avoid the use of naked pointers.

Last edited on
if you cant use vector and you still want dynamic array create linked list with struct student as value.

Linked list is easy to implement and it would sufficient your requirements.
I used the code that assumes max number of students and it worked. But still Andy and tath's solutions are also nice. (I tried all of them because our information sheet is not informing:D) Linked List usage would be the most proper way to use dynamic memory in my situation I guess(We haven't covered the topics about classes). Thank you all for your suggestions :)
> Linked List usage would be the most proper way to use dynamic memory in my situation I guess

No. std::vector<> would be the only 'proper' way if an upper bound on the number of students is not available.


JLBorges wrote:
No. std::vector<> would be the only 'proper' way if an upper bound on the number of students is not available.


Just wondering why you said that. Could I indulge you to educate me a little here? Hopefully the OP won't mind - could be good for him/her too. Edit: Also I think I have an understanding of how things work and should be used, then a very experienced person like yourself comes along and says the opposite!! The following is how I understand it at the moment :

If I had 2000 items, a std::list would be OK, but need to keep in mind whether there is any sorting or finding, inserting.

I could use a std::set or std::map, especially if there is lots of data, because these use AVL trees internally (does std::set use a BST?), so finding is more efficient. Need to be aware of the cost of any rebalancing of the tree that might happen, or if the data is already sorted.

I am just wary of std::vector because of it's inefficiencies when it gets resized. I am aware that it has a strategy of keeping it's capacity larger than what is required to mitigate this, but I can't help thinking that it is better to avoid reallocating memory altogether. It's especially bad for a vector of vectors. For these reasons, I have been avoiding vectors when there is no known upper bound, and is why I wanted to query you about this.

I suppose that a vector would be OK, if it was always going to be small and / or the size doesn't change very often.

I guess if there is only 500- 1000 items of data, then it probably won't matter a damn which container one uses.

Were these last 2 comments what you meant?
Last edited on
> I can't help thinking that it is better to avoid reallocating memory altogether. It's especially bad for a vector of vectors.

It is not the occasional reallocation of memory that is expensive; it does not occur all that often - for instance, if the vector size grows from zero to one million (without any call to reserve), and a typical doubling strategy is used, there would be 20 allocations of memory. (Teaser: how many memory allocations would a linked list make for the same?)

While the memory allocation is O(1), in the case of the vector, elements have to be moved to the newly allocated memory, and that is O(N). With C++11, copy_construction/expensive destruction is elided; std::is_nothrow_move_constructible<student>::value is true.

In this particular case, the number is fairly small (student information typed in by the user), the strong guarantee of exception safety is not required; std::vector<> is the appropriate choice. If the number was very large, a case could be made for std::deque<>.



> I could use a std::set or std::map, especially if there is lots of data,
> because these use AVL trees internally (does std::set use a BST?), so finding is more efficient.

Find is efficient in std::unordered_set<> and std::unordered_map<>. Find in a sorted vector (std::equal_range()) is actually faster than that in a binary search tree (better memory locality).
@JLBorges

Ah Ok, I keep forgetting about the move semantics in C++11.

I guess it all boils down to a design decision based on these things :

- Memory usage
- CPU time
- Is the data sorted already? If not the cost of sorting or increased finding time
- Do we need to do find, insert
- How much data? How big is an individual object?

Thanks for your info, I appreciate it. Cheers
The exact implementation of the STL not set in stone. The map on my computer is a RedBlack tree.
Pages: 12