2

I've got a task to get no. of students, get their name and marks and output the students which has average over 85.

The problem: After I enter blabla 99 98 95 90, I don't get the appropriate message. what I get is just some kind random of average instead. I mean, Print_One() isn't executed after that input. (Failing to print the average above 85)

Here's my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

typedef struct {
    char *name;
    int marks[4];
    float avg;
} student;

student *Create_Class(int);
void Avg_Mark(student*);
void Print_One(student*);
void exStudents(student *s, int size);

int main() {
    int size, i;
    student *arr;
    printf("\nEnter the number of students: \n");
    scanf("%d", &size);

    arr = Create_Class(size);
    exStudents(arr, size);

    for (i = 0; i < size; i++)
        free(arr[i].name);

    free(arr);
    getch();
}

student *Create_Class(int size) {
    int i, j;
    int idStud, nameStud, markStud;

    student *classStudent;
    classStudent = (student*)malloc(size * sizeof(student));

    for (i = 0; i < size; i++) {
        classStudent[i].name = (char*)malloc(51 * sizeof(char));
        int numOfmarks = 4;
        int sizeOfName;
        printf("Please enter your name: \n");
        flushall();
        gets(classStudent[i].name);

        sizeOfName = strlen(classStudent[i].name);
        /*
        if (classStudent[i].name > 50) {
            classStudent[i].name = realloc(classStudent[i].name, 51);
            classStudent[i].name[51] = '\0';
        } else {
            classStudent[i].name = realloc(classStudent[i].name, sizeOfName + 1);
        }
        */
        printf("Please enter 4 marks: ");
        for (j = 0; j < numOfmarks; j++) {
            scanf("%d", &classStudent[i].marks[j]);
        }
        Avg_Mark(&classStudent[i]);
    }
    return classStudent;
}

void Avg_Mark(student *s) {
    int i, numOfMarks = 4, sum = 0;

    for (i = 0; i < numOfMarks; i++) {
        sum += s->marks[i];
    }
    s->avg = (sum / 4.0);
}

void Print_One(student *s) {
    printf("The average of %s is %f", s->name, s->avg);
}

void exStudents(student *s, int size) {
    int flag = 1;

    while (size > 0) {
        if (s->avg > 85) {
            Print_One(s);
            flag = 0;
        }
        s++;
        size--;
    }
    if (flag)
        printf("\n There're no students with above 85 average.");
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Ilan Aizelman WS
  • 1,630
  • 2
  • 21
  • 44

2 Answers2

2

If your input is like this:

1
blabla
99 98 95 90

The first newline after 1 is still in the input buffer when your program reaches get, so an empty line is read and then the scanfs will fail.

An easy fix could be to read the first number using this format:

scanf("%d ", &size);
// note  ^ the space will consume the newline

But, as @chqrlie pointed out, "it will continue to read bytes from stdin until it sees one that is not whitespace. This will require the user to respond to the next question before the prompt is written."

A better idea is to read the name using another scanf, but limiting the maxium number of chars read to the allocated size and adding a space at the beginning of the format string to consume all pending whitespaces:

// read max 50 char till a newline and extract the rest of line without storing it
scanf(" %50[^\n]%*[^\n]", classStudent[i].name);
//    ^^^ a space at the beginning will also consume trailing spaces or newline
Bob__
  • 12,361
  • 3
  • 28
  • 42
  • `scanf("%50[^\n]%*s", classStudent[i].name);` will read an extra line if the student name is 50 characters or less and will leave some characters unread of the line is longer than 50 bytes but has more than one word after these 50 initial bytes. You should write `scanf("%50[^\n]%*[^\n]", classStudent[i].name);` to skip the rest of the line if longer than 50 bytes. – chqrlie Mar 18 '16 at 23:09
  • Sorry for the repeated remarks, but also note that `scanf("%d ", size);` is a bad idea: it will indeed consume the `\n` but it will continue to read bytes from `stdin` until it sees one that is not whitespace. This will require the user to respond to the next question before the prompt is written. To avoid reading the pending `\n` into the student name, an initial space in the next `scanf` to consume all whitespace is the correct solution: `scanf(" %50[^\n]%*[^\n]", classStudent[i].name);`. as you can see, `scanf` is **very** clunky. – chqrlie Mar 19 '16 at 09:53
  • @chqrlie every remarks is very welcome, I'll update the answer soon. Is there at least a project to improve the scanf family in the future standards? – Bob__ Mar 19 '16 at 10:04
1

It worked for me. All i did was using

_flushall();

instead of

flushall();