1
struct{
  int year;
  int unit;
  int NumberOfSubject;
  float gpa;
  char semester[5];
  char NameOfSubject[60][50];
  char grade;
  char name[40];
}student;

char CreatAccount(void)
{
    int i;
    char ans;
    database= fopen("database.rtf", "w");
    printf("\tThis is registration page\n");
    printf("Please enter your name\n");
    fgets(student.name,40,stdin);
    printf("Is your name %s?, Y/N\n", student.name);
    scanf("%c", &ans);
    if (ans == 'y' || ans == 'Y')
    {
        printf("Registration is successfully completed!. Name on your account is %s",student.name);
        fopen("database.txt", "w");
        for (i=0; student.name[i] != '\n'; i++)
        {
            fputc(student.name[i], database);
        }
        fclose(database);
            
        int i;
        int j;
        printf("Please enter year\n");
        scanf("%d",&(student.year));
        fflush(stdin);
        printf("Please enter semester\n");
        fgets(student.semester,10,stdin);
        printf("Please enter units you have taken\n");
        scanf("%d",&(student.unit));
        printf("Please enter number of subjects you are taking at this moment\n");
        scanf("%d",&(student.NumberOfSubject));
        fflush(stdin);
        for (i=0; i<student.NumberOfSubject; i++)
        {
            printf("Please enter the name of subjects you are taking\n");
            fgets(student.NameOfSubject[i],50,stdin);
            printf("Your %dth Subject is %s\n",i+1,student.NameOfSubject[i]);
        }
        printf("Registration is completed! Here is your information\n\n");
        printf("Your year:%d\n",student.year);
        printf("Semester:%s\n",student.semester);
        printf("Units:%d\n",student.unit);
        for (j=0; j<student.NumberOfSubject; j++)
        {
            printf("Name of subjects:%s",student.NameOfSubject[j]);
        }
        printf("Number of subjects:%d\n",student.NumberOfSubject);
    }

I just can't find the cause of this problem. When the output is printed out, there are things that shouldn't be there. I want to spot what the cause is for this matter. Thanks returnFor instance, if I enter following input

2022 -year

fall-semester

12-units

3-number of subjects

math,english,physics

the output will be this,

Your year:2022

Semester:fall

english // shouldn't be printed

    //shouldn't be next-line space here

Units:12

Name of subjects:english

Name of subjects:math

Name of subjects:physics

Number of subjects:3

  • 1
    `fflush(stdin);` is undefined behavior – Darth-CodeX May 10 '22 at 10:52
  • 1
    In a program calling `scanf`, do not also call `fgets` — it won't work properly. Trying to fix the problem with `fflush(stdin)` doesn't reliably help this problem. For now, use `scanf("%s")` to read names. When it's time to do better, see [What can I use for input conversion instead of scanf?](https://stackoverflow.com/questions/58403537) – Steve Summit May 10 '22 at 10:52
  • Also, `scanf("%c", &ans)` won't work reliably — use `scanf(" %c", &ans)` instead (note the extra space). – Steve Summit May 10 '22 at 10:55
  • @SteveSummit, Then "%d\n" will be the correct solution for this? – Johnny Song May 10 '22 at 10:59
  • @JohnnySong Well, no, `"%d\n"` with `scanf` is also wrong. See [this answer](https://stackoverflow.com/questions/72178518/how-can-i-fix-the-scanf-to-take-data-into-the-array/72178652#72178652) for some secret rules about `scanf`. – Steve Summit May 10 '22 at 11:02
  • @SteveSummit your reference answers perfectly. Thank you for your comment. – Johnny Song May 10 '22 at 11:30
  • @SteveSummit "For now, use `scanf("%s")` to read names. " is weak advice with its no-width specifier. – chux - Reinstate Monica May 10 '22 at 13:11

2 Answers2

0

fflush(stdin); is undefined behavior.

In fgets() newline character is present at length - 1 position.

Also, never ever mix scanf() with fgets().

size_t len = strlen(str); // str is your string
if(str[len - 1] == '\n')
    str[len - 1] = 0;

Edit: better one

str[strcspn(str, "\n")] = '\0'
Darth-CodeX
  • 2,166
  • 1
  • 6
  • 23
0

There are a couple of issues in your code:

  • You are mixing scanf with fget. They don't work the same way, so you shouldn't use them altogether (unless you are very careful).
  • fflush(stdin) is undefined behaviour. The only way to flush stdin is by reading what's in it.
  • scanf can be a dangerous function if you misuse it (and you clearly are). You need to check its return value and, in case it fails, perform some actions.
  • fgets reads also the newline (the character when you press Enter). You have to replace it with a null-terminator.

I just can't find the cause of this problem.

The last point above explains why.


scanf does not read user input. It parses it. That's what its name says: scan formatted. And scanning may fail, and when it does, it leaves the input buffer intact. So it's up to you to "flush" (i.e. clear, or read-and-discard everything in) it.

void fflush_stdin(void)
{
    scanf("%*[^\n]");
}

Addressing scanf failure is done by checking its return value. From cppreference:

Return value

Number of receiving arguments successfully assigned (which may be zero in case a matching failure occurred before the first receiving argument was assigned), or EOF if input failure occurs before the first receiving argument was assigned.

bool read_char(const char *msg, char *c)
{
    do {
        printf("%s", msg);
        int ret = scanf(" %c", c); // Note the extra space before %c. It means match all space characters before a given character is met.
        switch (ret) {
        case EOF: return false;
        case 1:   return true;
        default:
            flush_stdin();
            printf("Input error.\n");
            break;
        }
    } while (true);
    
    return true; // This won't run.
}

The same thing goes for ints, floats, etc. For scanning strings, you have to provide the length of the char buffer:

char buffer[100];
scanf("%99[^\n]", buffer);
Zakk
  • 1,935
  • 1
  • 6
  • 17
  • I read your reference and learned that to use scanf to read, I need to put whitespace before the format specifier. Thanks for your comment. Another learning! – Johnny Song May 18 '22 at 12:51