Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You can either use a linked list instead of the array as Luchian Grigore suggests, or you can use dynamically allocated array of structures, or you can use a dynamically allocated array of pointers to dynamically allocated structures. The advantage of the latter is that there is less copying to do when the array is allocated; the disadvantage is that there are more memory allocations to release.</p> <p>How does this translate into code? This is an outline of the 'dynamically allocated array of pointers to dynamically allocated structures'. I hypothesize a routine to read a single student's information; it will allocate a structure with the right number of marks too. Basically, it reads a line from the file (spotting EOF and returning a null pointer when the sentinel value is read). For a student, it allocates a student mark structure, and an array of integers of the right size. It parses the line for the student number and each grade. It can report an error if there are marks missing. It returns the pointer to the allocated student. I assume there are two separate allocations &mdash; one for the student marks structure and a separate one for the array of marks.</p> <pre><code>typedef struct StudentMarks Marks; static Marks *read_student_marks(FILE *fp, int num_grades); Marks **marks = 0; /* Array of pointers to student marks */ size_t num_marks = 0; /* Number of marks in use */ size_t max_marks = 0; /* Number of marks allocated */ Marks *student; while ((student = read_student_marks(fp, num_grades)) != 0) { assert(num_marks &lt;= max_marks); if (num_marks == max_marks) { /* Not enough space left - allocate more */ size_t new_size = max_marks * 2 + 2; Marks **new_marks = realloc(marks, new_size * sizeof(*marks)); if (new_marks == 0) ...handle out of memory error... ...NB: you still have the original array to work with... marks = new_marks; max_marks = new_size; } marks[num_marks++] = student; } </code></pre> <p>This starts with a small allocation (2 entries), so that the reallocation code is exercised. I've also exploited the fact that if you pass a NULL pointer to <code>realloc()</code>, it will allocate new memory. An alternative version would use <code>malloc()</code> for the initial allocation and <code>realloc()</code> thereafter:</p> <pre><code>size_t num_marks = 0; size_t max_marks = 2; Marks **marks = malloc(max_marks * sizeof(*marks)); if (marks == 0) ...handle out of memory condition... while ((students = ... </code></pre> <p>If you're worried about having allocated too much space at the end of the loop, you can release the surplus with:</p> <pre><code>marks = realloc(marks, num_marks * sizeof(*marks)); max_marks = num_marks; </code></pre> <p>The release code is:</p> <pre><code>for (i = 0; i &lt; num_marks; i++) { free(marks[i]-&gt;assignMark); free(marks[i]); } free(marks); marks = 0; num_marks = 0; max_marks = 0; </code></pre> <p>If you want to allocate the array of student marks, you need to decide how the student reader function is going to work. You can use the design outlined above; you'd copy the returned structure into the allocated array. You then release just the marks structure (but not the array of marks; that is still in use). You can grow the array in very much the same way as I did above. The release code is different (simpler), though:</p> <pre><code>for (i = 0; i &lt; num_marks; i++) free(marks[i]-&gt;assignMarks); free(marks); marks = 0; num_marks = 0; max_marks = 0; </code></pre> <hr> <p>ASCII art to the rescue &mdash; maybe...</p> <p>The first code assumes you get a pointer to a set of marks for a student from the <code>read_student_marks()</code> function. It keeps an array of pointers to those entries:</p> <pre><code>+----------+ +--------------------+ | marks[0] |--------&gt;| marks for 12000001 | +----------+ +--------------------+ +--------------------+ | marks[1] |------------------------------------&gt;| marks for 12000002 | +----------+ +--------------------+ +--------------------+ | marks[2] |--------&gt;| marks for 12000003 | +----------+ +--------------------+ +--------------------+ | marks[3] |------------------------------------&gt;| marks for 12000004 | +----------+ +--------------------+ </code></pre> <p>Notice how the <code>marks</code> array is contiguous, but the marks for each student are separately allocated. The code periodically reallocates the <code>marks</code> array when it needs to grow, but does not move the individual marks for each student.</p> <p>The alternative scheme outlined is like this:</p> <pre><code>+--------------------+ | marks for 12000001 | +--------------------+ | marks for 12000002 | +--------------------+ | marks for 12000003 | +--------------------+ | marks for 12000004 | +--------------------+ </code></pre> <p>Here you would be reallocating all the memory, copying the complete marks structures around. At some levels, this is simpler than the scheme I outlined first.</p> <p>Complicating this discussion is the fact that the 'marks for' structures are not as simple as I showed:</p> <pre><code>+--------------------------+ | studentNumber: 12000001 | +------+------+------+ | assignMark: *--------=-----------&gt;| 52 | 45 | 60 | | moduleMark: 92 | +------+------+------+ +--------------------------+ </code></pre> <p>If you have a C99 compiler, you could use a stucture with a 'flexible array member' for the last element of the structure; this would hold the marks:</p> <pre><code>struct StudentMarks { int studentNumber; int moduleMark; int assignMark[]; // Flexible array member } </code></pre> <p>You would allocate this using a notation such as:</p> <pre><code>struct StudentMarks *marks = malloc(sizeof(*marks) + num_assignments * sizeof(marks-&gt;assignMark[0])); </code></pre> <p>This allocates the student mark structure and the array in a single memory allocation. However, you cannot have an array of structures with a flexible array member; you can only use an array of pointers to a structure with a flexible array member (getting you back to the first code I showed).</p> <p>In case you haven't already gathered, there is more than one way to do it &mdash; which is the motto of Perl (aka TMTOWTDI), but applies well here.</p> <p>As some advice, I recommend drawing diagrams of pointers similar to those I made to help you understand what you are doing. After a while, they'll cease to be necessary, but diagrams can be a great help while you need them.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload