-1

Here is a part of the program:

 17 #include<stdio.h>
 18 
 19 struct Student
 20 {
 21   int number;
 22   char name[15];
 23 }student1,student2,student3,student4;
 24 
 25 void input_name();
 26 
 27 void input_name()
 28 {
 29   int h,i,j,k;
 30   printf("********************************\n");
 31   printf("*   Enter student information  *\n");
 32   printf("********************************\n");
 33   printf("Please enter the student number of the first student: ");
 34   scanf("%d",&student1.number);
 35   printf("Please enter the first student's name: ");
 36   while(1)
 37   {
 38     for(h=0;h<15;h++)
 39     {
 40       scanf("%c",&student1.name[h]);
 41       if(student1.name[h]==13)
 42       {
 43         student1.name[h]='\0';
 44         break;
 45       }
 46     }
 47   }
 48   printf("Please enter the student number of the second student: \n");
 49   scanf("%d",&student2.number);
 50   printf("Please enter the second student's name: \n");
 51   while(1)
 52   {
 53     for(i=0;i<15;i++)
 54     {
 55       scanf("%c",&student2.name[i]);
 56       if(student2.name[i]==13)
 57       {
 58         student2.name[i]='\0';
 59         break;
 60       }
 61      }
 62   }

Lines 33 to 47 can be executed, but lines 48 to 62 can't be executed. When I press the Enter key, I keep breaking lines. So, I want to ask why the latter part can't be implemented, and which link in the middle has a problem?

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
HuangZiHan
  • 25
  • 4
  • 1
    The `break` statements don't do what you probably intended. – aschepler Feb 19 '23 at 12:47
  • 1
    break is only from innermost loop – stark Feb 19 '23 at 12:49
  • 2
    Please don't include line-numbers in your [mre], that makes is much harder for us to try it out ourselves. If you have a section or a few lines you want to highlight, mention them in the question body and add comments on those lines in the code. – Some programmer dude Feb 19 '23 at 12:50
  • Side note: Instead of using a loop of `scanf` with the `"%c"` format string, you could simply use a single call to the function [`fgets`](https://en.cppreference.com/w/c/io/fgets) and then remove the newline character [like this](https://stackoverflow.com/q/2693776/12149471). In that case, the inner loop would be redundant. – Andreas Wenzel Feb 19 '23 at 12:52
  • @HuangZiHan You have infinite while loops within the function. – Vlad from Moscow Feb 19 '23 at 12:52
  • @Andreas Wenzel Output student's student number and name – HuangZiHan Feb 19 '23 at 13:01
  • @Some programmer dude I understand. I will pay attention next time. Thank you very much for your reminding. – HuangZiHan Feb 19 '23 at 13:03
  • @ Andreas Wenzel I'm used to using scanf statements. I'll try your method later. – HuangZiHan Feb 19 '23 at 13:06
  • 1
    Have you tried running your code line-by-line in a debugger while monitoring the control flow and the values of all variables, in order to determine in which line your program stops behaving as intended? If you did not try this, then you may want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) – Andreas Wenzel Feb 19 '23 at 13:16
  • @Andreas Wenzel I use gcc in the Linux environment on the cloud server for programming, and generally modify my code according to warnings or errors.Now I'm looking at the link you gave. – HuangZiHan Feb 19 '23 at 13:30
  • @all After reading your answers and combining my previous experience, I have solved the problem now.Thank you very much for taking the time to answer my questions. – HuangZiHan Feb 19 '23 at 13:36
  • @HuangZiHan: I am currently writing an answer for you, which will point out additional problems. I will need about 20 minutes for posting my answer. – Andreas Wenzel Feb 19 '23 at 13:40
  • What is the point of the value `13` in the line `if(student1.name[i]==13)`? That is the ASCII code for carriage return, i.e. for `\r`. However, standard input should be open in text mode according to the C standard, so you should only see a newline charcter `\n`, which has the ASCII code `10` instead of `13`. However, I have seen at least one environment which gives you a `\r\n` when the user presses the `ENTER` key. Are you using such an environment? Or why are you writing `13` instead of `\n` (which is `10`)? – Andreas Wenzel Feb 19 '23 at 14:04
  • @Andreas Wenzel There is no prompt. Just press the Enter key to break the line. I have noticed the problem of 13 and 10, but I used 10 before that. My programming environment is not an integrated environment, but a power shell similar to Windows. – HuangZiHan Feb 19 '23 at 14:35

1 Answers1

1

The first loop (lines 36-47) has the following problems:

  1. You are not taking into account that line 34 left a newline character on the input stream, so the first character read in line 40 will be this leftover newline character. It is also possible that other characters were left on the input stream. For example, if the user entered 6abc on line 34, then scanf on line 34 will only consume the 6, but leave abc\n on the input stream.
  2. The break statement will only break out of the innermost loop, but not the outer loop.
  3. If the user enters at least 15 characters (not including the newline character), then the string you write will not be null-terminated and your program will not consume all characters of the line of user input.

The second loop (lines 51-62) has the same problems.

In order to solve issue #1, you should discard the remainder of the line, including the newline character, for example like this:

//discard all leftover characters up to the newline character
scanf( "%*[^\n]" );

//discard the newline character itself
scanf( "%*c" );

Or like this:

int c;

do
{
    c = getchar();

} while ( c != EOF && c != '\n' );

To solve issue #2, you should remove the outer while(1) loop.

To solve issue #3, you should exit the program with an error message, instead of continuing the program.

Here is the fixed code:

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

struct Student
{
    int number;
    char name[15];
} student1, student2, student3, student4;
 
int main( void )
{
    printf( "********************************\n" );
    printf( "*   Enter student information  *\n" );
    printf( "********************************\n" );

    //first student

    printf( "Please enter the student number of the first student: " );
    scanf( "%d", &student1.number );

    //discard remainder of line
    scanf( "%*[^\n]" );
    scanf( "%*c" );

    printf( "Please enter the first student's name: " );
    for( int h = 0; ; h++ )
    {
        if ( h == 15 )
        {
            printf( "Input is too long for input buffer!\n" );
            exit( EXIT_FAILURE );
        }

        scanf( "%c", &student1.name[h] );
        if( student1.name[h] == '\n' )
        {
            student1.name[h] = '\0';
            break;
        }
    }

    //second student

    printf( "Please enter the student number of the second student: " );
    scanf( "%d", &student2.number );

    //discard remainder of line
    scanf( "%*[^\n]" );
    scanf( "%*c" );

    printf( "Please enter the second student's name: " );
    for( int h = 0; ; h++ )
    {
        if ( h == 15 )
        {
            printf( "Input is too long for input buffer!\n" );
            exit( EXIT_FAILURE );
        }

        scanf( "%c", &student2.name[h] );
        if( student2.name[h] == '\n' )
        {
            student2.name[h] = '\0';
            break;
        }
    }

    //print back the information

    printf( "\nYou entered the following information:\n\n" );

    printf( "Student 1:\n" );
    printf( "Number: %d\n", student1.number );
    printf( "Name: %s\n", student1.name );
    printf( "\n" );

    printf( "Student 2:\n" );
    printf( "Number: %d\n", student2.number );
    printf( "Name: %s\n", student2.name );
    printf( "\n" );
}

This program has the following behavior:

********************************
*   Enter student information  *
********************************
Please enter the student number of the first student: 67
Please enter the first student's name: Mike
Please enter the student number of the second student: 78
Please enter the second student's name: Jimmy

You entered the following information:

Student 1:
Number: 67
Name: Mike

Student 2:
Number: 78
Name: Jimmy

Note however that your code has a lot of code duplication, and it will get worse if you add the code for the third and fourth students. Therefore, it would be better to handle all students in a loop, by creating an additional outer loop:

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

#define NUM_STUDENTS 4
#define MAX_NAME_LENGTH 15

struct Student
{
    int number;
    char name[MAX_NAME_LENGTH];
};
 
int main( void )
{
    struct Student students[NUM_STUDENTS];

    static const char *numeral_adjectives[] =
    {
        "zeroth", "first", "second", "third", "fourth",
        "fifth", "sixth", "seventh", "eighth", "ninth"
    };

    printf( "********************************\n" );
    printf( "*   Enter student information  *\n" );
    printf( "********************************\n" );

    for ( int i = 0; i < NUM_STUDENTS; i++ )
    {
        printf(
            "Please enter the student number of the %s student: ",
            numeral_adjectives[i+1]
        );
        scanf( "%d", &students[i].number );

        //discard remainder of line
        scanf( "%*[^\n]" );
        scanf( "%*c" );

        printf(
            "Please enter the %s student's name: ",
            numeral_adjectives[i+1]
        );
        for( int j = 0; ; j++ )
        {
            if ( j == MAX_NAME_LENGTH )
            {
                printf( "Input is too long for input buffer!\n" );
                exit( EXIT_FAILURE );
            }

            scanf( "%c", &students[i].name[j] );
            if( students[i].name[j] == '\n' )
            {
                students[i].name[j] = '\0';
                break;
            }
        }
    }

    //print back the information

    printf( "\nYou entered the following information:\n\n" );

    for ( int i = 0; i < NUM_STUDENTS; i++ )
    {
        printf( "Student %d:\n", i+1 );
        printf( "Number: %d\n", students[i].number );
        printf( "Name: %s\n", students[i].name );
        printf( "\n" );
    }
}

This program has the following behavior:

********************************
*   Enter student information  *
********************************
Please enter the student number of the first student: 67
Please enter the first student's name: Mike
Please enter the student number of the second student: 78
Please enter the first student's name: Jimmy
Please enter the student number of the third student: 105
Please enter the first student's name: Jane
Please enter the student number of the fourth student: 112
Please enter the first student's name: Bob 

You entered the following information:

Student 1:
Number: 67
Name: Mike

Student 2:
Number: 78
Name: Jimmy

Student 3:
Number: 105
Name: Jane

Student 4:
Number: 112
Name: Bob

Note that when dealing with line-based user input, it is generally better to use fgets instead of scanf. In contrast to scanf, fgets will always read an entire line at once, unless it is unable to do so, due to the size of the supplied memory buffer not being large enough.

That way, you won't have the problem of scanf reading partial lines whose leftovers you must discard.

Here is an example program which uses fgets instead of scanf:

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

#define NUM_STUDENTS 4
#define MAX_NAME_LENGTH 15

struct Student
{
    int number;
    char name[MAX_NAME_LENGTH];
};
 
int main( void )
{
    struct Student students[NUM_STUDENTS];

    static const char *numeral_adjectives[] =
    {
        "zeroth", "first", "second", "third", "fourth",
        "fifth", "sixth", "seventh", "eighth", "ninth"
    };

    printf( "********************************\n" );
    printf( "*   Enter student information  *\n" );
    printf( "********************************\n" );

    for ( int i = 0; i < NUM_STUDENTS; i++ )
    {
        char input[200];
        char *p;

        //prompt user for student's number
        printf(
            "Please enter the student number of the %s student: ",
            numeral_adjectives[i+1]
        );

        //read exactly one line of input
        fgets( input, sizeof input, stdin );

        //convert input string to a number
        students[i].number = strtol( input, NULL, 10 );

        //prompt user for student's name
        printf(
            "Please enter the %s student's name: ",
            numeral_adjectives[i+1]
        );

        //attempt to read exactly one line of input
        fgets( students[i].name, sizeof students[i].name, stdin );

        //attempt to find newline character in input
        p = strchr( students[i].name, '\n' );

        //verify that the newline character is present,
        //because otherwise, we must assume that the input
        //was too long to fit into the memory buffer
        if ( p == NULL )
        {
            printf( "Input is too long for input buffer!\n" );
            exit( EXIT_FAILURE );
        }

        //remove the newline character by overwriting it
        //with a null character
        *p = '\0';
    }

    //print back the information

    printf( "\nYou entered the following information:\n\n" );

    for ( int i = 0; i < NUM_STUDENTS; i++ )
    {
        printf( "Student %d:\n", i+1 );
        printf( "Number: %d\n", students[i].number );
        printf( "Name: %s\n", students[i].name );
        printf( "\n" );
    }
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Thank you very much. I can see that you love programming very much. I want to learn from you. – HuangZiHan Feb 19 '23 at 14:53
  • @HuangZiHan: I have now added another solution to my answer. The last solution uses [`fgets`](https://en.cppreference.com/w/c/io/fgets) instead of `scanf`. – Andreas Wenzel Feb 19 '23 at 16:08
  • @HuangZiHan: If my solution does not work with you, then it is possibly because your environment is using `\r\n` instead of `\n` for line endings. See my comments to your question for further information. If that is the case, then please tell me. The reason I am unsure is because in the code in your question, you are using ASCII Code `13` (carriage return) instead of ASCII Code `10` (line feed) for line endings. – Andreas Wenzel Feb 20 '23 at 13:09