A Table of Records

One struct holds one student; a class holds many. An array of structures is exactly what the words promise, elements that are records, and it is the shape of every roster, inventory, and results table in C, the exam's favorite long-form program, and the direct ancestor of the database row. Everything from chapters 7 and 10 composes here, with nothing new to memorize.

Declaring and Walking

Read students[i].roll inside out, and note the order: index first, then member. students[i] is one struct Student; the dot then selects within it. Nested braces initialize record by record, mirroring 2D arrays' rows, with the same zero-the-rest rule. The walking loop, the accumulator, the cast-before-divide average: every piece is an old friend wearing a record.

Reading Records

Filling the table from input is the chapter 7 pattern with a struct element type, guards included:

Count validated against capacity before reading, each record's reads guarded, &students[i].roll composing index, dot, and address-of exactly as the pieces suggest. Try it with 2 students. Nothing here is new; that is the point.

The Classic Queries

With records in a table, the exam questions are searches and extremes from chapter 7, re-keyed to a member. Finding the topper:

int best = 0;

for (i = 1; i < count; i++) {
    if (students[i].marks > students[best].marks) {
        best = i;
    }
}
printf("topper: roll %d with %d\n", students[best].roll, students[best].marks);

The technique to notice: track the index of the best record, not a copy of it, so the answer carries every member, roll and marks, when found. Linear search by roll number is the same loop with == on students[i].roll; sorting by marks is chapter 7's bubble sort where the comparison reads .marks and the swap moves whole structs, three assignments through a struct Student temp, legal precisely because structs assign. One member decides the order; the entire record moves together, the line that separates a right answer from a scrambled table.

Arrays Inside the Records

A member can itself be an array, and the moment a student carries three subject marks instead of one, that is the natural shape:

Two subscripts now, indexing two different things: students[i] picks a record out of the table, and .marks[j] picks an element out of that record's array. Read students[i].marks[j] strictly left to right, table then member then element, and the sentence exams want writes itself: students[1].marks[2] is the third subject's mark for the second student. You have met the feature already without naming it, since a char name[20] member is exactly this.

The initializer braces nest one level deeper, one inner pair per record's array. Each student's total lives in the struct as a member of its own, so the computed answer travels with the record it describes; the subject-wise totals need a separate plain array precisely because they belong to no single student. The inner loop over SUBJECTS inside the outer loop over records is chapter 7's two-dimensional traversal with a record wrapped around one of the dimensions.

count came from sizeof(students) / sizeof(students[0]), chapter 7's element-count idiom applied to a table of records: all the bytes divided by one record's bytes. It is the honest way to size a brace-initialized table whose count you never wrote down, and it stays right when you add a record. It works only where the array itself is in scope, though, never on a table a function received as a parameter, for a reason chapter 11 makes plain.

Records Versus Parallel Arrays

The alternative design, one int rolls[50] beside one int marks[50], keeps related values in parallel arrays, and pre-struct code did exactly that. It works until anything reorders or resizes one array without the other, at which point every record silently decouples. The struct array keeps each record's members welded together through every swap, sort, and copy, which is the design argument exams ask you to make: group what moves together.

Key Takeaways

  • struct Student students[N] is a table of records: index first, member second, students[i].marks, and &students[i].roll scans straight into a record.
  • Nested braces initialize record by record with the zero-the-rest rule; the capacity/count discipline from chapter 7 applies unchanged.
  • Track indexes in search loops so the whole winning record is available; sort by comparing one member while swapping whole structs via a struct temp.
  • Struct assignment is what makes record swaps three clean lines; one member orders, the record travels.
  • A member can be an array: students[i].marks[j] indexes the table, then the record, then the element, and char name[20] members are the same feature.
  • sizeof(students) / sizeof(students[0]) counts the records in a table that is in scope, which is how a brace-initialized table reports its own size.
  • Parallel arrays decouple under reordering; structs weld each record together: group what moves together.