The first loop (lines 36-47) has the following problems:
- 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.
- The
break
statement will only break out of the innermost loop, but not the outer loop.
- 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" );
}
}