Creating a simple C function that prevents the user from entering anything other than a number between 1 and 9. Any other input including letters symbols and any number that is either less than 1 and greater than 9 should not be accepted.
So far its pretty straight forward; however, the part where the code is supposed to check that the entered character is not a symbol or letter is not working the way I wanted it to work.
int validateUserInput(){
printf("%s\n", "Please enter a number from 1 to 9: ");
char value = getchar();
int numValue = value;
char temp;
int digitCounter = 0;
while((temp = getchar()) != '\n'){
digitCounter++;
}
//if there is more than 1 digit.
if(digitCounter>0){
printf("%s\n","Input too long!");
return validateUserInput();
}
// if the char entered is not between 1 and 9
// this part is giving me a hard time.
else if(numValue < 49 || numValue > 57){
printf("%s\n", "Imput is not within the valid parameters");
return validateUserInput();
}
return numValue;
}
There are a couple of additional points to look at when taking input with your input routine. What if the user needs to cancel input? (e.g. presses ctrl+d, or ctrl+z on windows) As you have it written, there is no way to cancel the loop. (while that works for your purpose of forcing only a single input of 1-9, it leaves no way to cancel) If you trap EOF, you provide both, a way to cancel, and a way to indicate cancellation back in the calling function (e.g. by checking the return against EOF)
While recursion has its place, be aware that each recursion is itself a separate function call that requires a separate stack, and all the other trappings of a function call. There are many times when that can be avoided with a simple goto statement. glibc makes regular use of goto (e.g., check the source of qsort, getdelim, etc..) In your case, a single goto label can completely eliminate the need for recursion. For example, you could do something similar to the following while meeting all of your criteria:
int validateuserinput()
{
int c, extra, tmp;
getinput:; /* a simple goto can avoid recursion completely */
extra = 0;
printf ("Please enter a number from 1 to 9: ");
/* prompt/loop while chars not between 1 and 9 */
for (c = getchar(); (c < '1' || '9' < c ); c = getchar()) {
if (c == '\n') { /* no digits in input */
fprintf (stderr, "error: invalid input.\n");
goto getinput;
}
else if (c == EOF) { /* trap EOF */
fprintf (stderr, "\nerror: input canceled.\n");
return c;
}
}
/* empty input buffer -- increment extra count */
for (tmp = getchar(); tmp != '\n' && tmp != EOF; tmp = getchar())
extra++;
if (extra) { /* if extra chars -- input too long */
fprintf (stderr, "error: input too long.\n");
goto getinput;
}
return c - '0'; /* return integer value instead of ASCII value */
}
(just a style note, C generally avoids the use of camelCase variable names in favor of lower-case, that's just a generality, it's entirely up to you)
You can check the function (and respond to a cancellation) with the following short bit of code:
#include <stdio.h>
int validateuserinput();
int main (void) {
int n;
if ((n = validateuserinput()) != EOF)
printf ("\n valid input : %d\n", n);
return 0;
}
Example Use/Output
Testing accepting input of only 1-9:
$ ./bin/inputhandler
Please enter a number from 1 to 9: Hello World!
error: invalid input.
Please enter a number from 1 to 9: ?
error: invalid input.
Please enter a number from 1 to 9: 0
error: invalid input.
Please enter a number from 1 to 9: 23
error: input too long.
Please enter a number from 1 to 9: 6
valid input : 6
Testing input cancellation (e.g. ctrl+d, or ctrl+z on windows)
$ ./bin/inputhandler
Please enter a number from 1 to 9:
error: input canceled.
While there is nothing wrong with using recursion, it always helps to ask "Does this need to be a recursive function to begin with?" Sometimes the answer will be yes, but often there are simple ways to avoid the additional overhead. (note: the overhead with a few recursions is minimal so it isn't a big consideration in your case, but if you unwittingly call a recursive function that spins a million times, it rapidly becomes a concern)
Look over all answers, and if you have any questions, just let us know.
You only set numValue once - for the first getchar() - looks like you expect it to be set after every getchar().
while((temp = getchar()) != '\n'){
digitCounter++;
}
could be
while((temp = getchar()) != '\n'){
if (temp < 49 || temp > 57 || ++digitCounter>0)
return validateUserInput();
}
This way makes it simple I think
Related
Beginner here.
In the ANSI C textbook by K&R, page 20, they ask: How would you test the word count program?
I have copied exactly from the text book, using the CodeBlocks IDE, console application. I have seen many great input tests online, but my question is even dumber. How do I actually input something? Nothing happens when I press enter. Do I have this problem because I am using an IDE and therefore not learning how to run C programs properly?
Thanks in advance. I added a picture to show you what I mean
Here is the code:
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* counts lines, words and characters as input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
/* set these three constants to 0: */
nl = nw = nc = 0;
while ((c = getchar()) != EOF){
++nc;
if (c == '\n')
++nl;
/* || == OR (&& == AND)
evaluation of the following line
will stop as soon as the truth or
falsehood is known, so the order matters */
if (c == ' ' || c == '\n' == c == '\t')
state = OUT;
else if (state == OUT){
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
On MacOS:
gcc kr_wc.c -o kr_wc
./kr_wc < example_text.txt
Example output:
40 260 1397
Where example_text.txt is this file:
1.5.4 Word Counting
The fourth in our series of useful programs counts lines, words, and
characters, with the loose definition that a word is any sequence of
characters that does not contain a blank, tab or newline. This is a
bare-bones version of the UNIX program wc.
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
int main() {
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c == '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
Every time the program encounters the first character of a word, it
counts one more word. The variable state records whether the program
is currently in a word or not; initially it is "not in a word", which
is assigned the value OUT. We prefer the symbolic constants IN and
OUT to the literal values 1 and 0 because they make the program more
readable. In a program as tiny as this, it makes little difference,
but in larger programs, the increase in clarity is well worth the
modest extra effort to write it this way from the beginning. You'll
also find that it's easier to make extensive changes in programs
where magic numbers appear only as symbolic constants.
The program to count words in K&R 2nd edition is made to run on an environment in which you signal the end of input somehow. Normally, as they used UNIX all the time, they used Ctrl-D sequence (which is valid if you run the program in Linux or any Unix-like operating system) This has been so since the early beginning of the UNIX system.
Windows signals the end of input in a console application by the input of a Ctrl-Z (probably followed by a keyboard return key)
If you redirect the input from a file (as when you say a.out <my_input_file.txt) you'll get the number of words at the end, when there's no more input in the file.
You are running the program in an IDE, which is something that normally hides you where standard input and standard output go, or how to signal the window you show how to say that there's not more input to the program.
For the program to get to it's end, you have first to know how to get to an end on input.
The examples in K&R omit a return type of main, which is not valid in modern C, so add int before main():
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* counts lines, words and characters as input */
int main()
{
int c, nl, nw, nc, state;
state = OUT;
/* set these three constants to 0: */
nl = nw = nc = 0;
while ((c = getchar()) != EOF){
++nc;
if (c == '\n')
++nl;
/* || == OR (&& == AND)
evaluation of the following line
will stop as soon as the truth or
falsehood is known, so the order matters */
if (c == ' ' || c == '\n' == c == '\t')
state = OUT;
else if (state == OUT){
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
"How do I actually input something? Nothing happens when I press enter."
If you got problems with your IDE just run it online.
"How would you test the word count program"?
To cite the authors of a bundle of K&R solutions with an answer to that particular question here:
It sounds like they are really trying to get the programmers to learn how to do a unit test. I would submit the following:
input file contains zero words
input file contains 1 enormous word without any newlines
input file contains all white space without newlines
input file contains 66000 newlines
input file contains word/{huge sequence of whitespace of different >kinds}/word
input file contains 66000 single letter words, 66 to the line
input file contains 66000 words without any newlines
input file is /usr/dict contents (or equivalent)
input file is full collection of moby words
input file is binary (e.g. its own executable)
input file is /dev/null (or equivalent)
66000 is chosen to check for integral overflow on small integer machines.
I'm writing a C program which reads a text file line by line with a certain format to it.
I made a do { ... } while(!feof(file)); loop but it always loops one too many times. This is an issue because I have made it so that when my program expects to read something but gets nothing, it throws an error, so now it is throwing an error every time because it reaches the end of the file at the top of my loop.
I figured this is because the eof flag is triggered only once you try to fscanf something but there is nothing there. How can I fix this problem? Putting a final fscanf at the bottom doesn't work because if it's not at the end of the file, it will mess all the readings up and shift everything by one.
do {
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
// Read grades
read = fscanf(/*...*/);
if (read != 3)
{
return -1;
}
// Read student kind
int student_kind = 0;
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
if (student_kind < 0 | student_kind > 2)
{
printf("Invalid student kind");
return -1;
}
SCIPER sciper_teammate = 0;
read = fscanf(/*...*/);
if (read != 1)
{
return -1;
}
} while (!feof(file));
Since you are using fscanf():
ISO/IEC 9899:2017
§ 7.21.6.2 - 16 - The fscanf function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.
EOF is a macro with the value of -1, by itself it's not distinguishable as for the reasons why it occurs.
For this distinction § 7.21.6.2 - 19 recommends the use of feof() for end-of-file and ferror() for I/O error:
EXAMPLE 3 To accept repeatedly from stdin a quantity, a unit of measure, and an item name:
#include<stdio.h>
/*...*/
int count; floatquant;
charunits[21], item[21];
do {
count = fscanf(stdin, "%f%20sof%20s", &quant, units, item);
fscanf(stdin,"%*[^\n]"); //here discarding unread characters in the buffer
} while(!feof(stdin) && !ferror(stdin));
This should work in your case but personaly. I don't like this approach since if you input less values than what fscanf is expecting this will fail, normaly resulting in an infinite loop.
My approach when reading formated input, is to check the inputed values.
For a sample input of 2 integers you can do something like:
Live sample
#include <stdio.h>
int main()
{
int a, b;
FILE* file;
if(!(file = fopen("file.txt", "r"))){
return 1;
}
while(fscanf(file, "%d %d", &a, &b) == 2){ //read each 2 integers in the file, stop when condition fails, i.e. there are nothing else to read or the read input is not an integer
printf("%d %d\n", a, b);
}
}
This addresses all input failures and will end the cycle for I/O error, for EOF and for bad inputs.
Screen shot showing problem
I am trying to print output from user by traversing an array in using the C programming language.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//my global variables
char day[15];
int dailyTrainCount[5]; // variable for daily train count
int trainTimeFrame[5], i; // variable to daily train time frames and loop control
//begin program
int main()
{
printf("Welcome to TMS(TRAIN MANAGEMENT SYSTEM)\n");
printf("Enter day of Operation\n");
scanf("%s", day);//user input for day
printf("How many trains to operate on %s?(Trains shall not exceed 5)\n",
day);
scanf("%d", &dailyTrainCount[5]);//daily train count variable
//loop to ensure user enters no more than 5 trains per day
while(dailyTrainCount[5] > 5){
printf("Daily train count shall not exceed 5\n");
printf("enter train count\n");
scanf("%d", &dailyTrainCount[5]);
}
//show train menu to user
printf("%d Train(s) will operate on %s\n",dailyTrainCount[5], day);
printf("Available time frames\n");
printf("(1):7AM-10PM\n");
printf("(2):10AM-1PM\n");
printf("(3):1PM-4PM\n");
printf("(4):4PM-7PM\n");
for(i=0; i<dailyTrainCount[5]; i++)
{
printf("Enter time frame for train #%d: \n",i+1);
scanf("%d", &trainTimeFrame[5]);
}
//This loop is not traversing through the entire array, it only shows the last two input
for(i=0; i<dailyTrainCount[5]; i++)
{
printf("Train #%d is set to time frame #%d\n", // This is the print statement that
// is not producing the output as I intended
dailyTrainCount[5],
trainTimeFrame[5]);
}
return 0;
}
How can I produce output for the last for loop correctly? I am using Code Blocks. I've also tried changing all the arrays to [i], instead of [5].
You have fundamental misunderstanding of the use of arrays in C:
int dailyTrainCount[5]; // variable for daily train count
...
scanf("%d", &dailyTrainCount[5]);
int dailyTrainCout[5]; declares an integer array with 5 elements. The valid indexes for those elements are [0-4], C/C++ use zero based arrays. By assigning to dailyTrainCount[5], you assign the input to the memory location 1-past the end of the dailyTrainCount array, invoking Undefined Behavior. (the defined execution of your program ceases at that point and anything from appearing to work normally to SegFaulting can occur) More fundamental, there will only be 1 dailyTrainCount so an array is not needed to begin with.
Next, while not an error, avoid the use of global variables. They are rarely required. Instead, declare your variables in the scope where they are needed and pass the variables as parameters to any function that needs to operate on their data.
Using scanf for input by new C programmers accounts for a significant percentage of of questions on this site. The misuse of scanf is common. The most common misuse is failing to check the return. The second most common is failing to account for what characters remain in the input buffer, unread, after a matching failure.
scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions
(return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or Ctrl+z on windows);
(return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
(return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).
Note: after either a matching failure or successful read, you should empty the input buffer to ensure is it is prepared for the next user input, regardless of what function is used to take the input. For example, if you successfully use scanf to take integer input, but leave the trailing '\n' in the input buffer and then later attempt input with fgets, your input will fail and your buffer will contain the empty-string no matter how many characters the user types...
To facilitate using scanf correctly, you can use a short helper-function to empty the characters from stdin after each input. After each input, you simply call getchar() to read until the '\n' or EOF is found, e.g.
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Then, at minimum, you must validate the return of scanf matches the number of conversions specified in your format-string, e.g.
char day[MAXDAY];
int /* dailyTrainCount[5],*/ trainsperday,
trainTimeFrame[MAXTRAIN], i = 0;
fputs ("Welcome to TMS (TRAIN MANAGEMENT SYSTEM)\n\n"
" Enter day of Operation: ", stdout);
if (scanf ("%14s", day) != 1) { /* VALIDATE EVERY INPUT - check return */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
(note: your dailyTrainCount[] array was replaced with a single integer variable trainsperday to make the change from array of int to single integer clear. empty_stdin() protects against any extraneous characters entered, such as the user entering "Monday April 22, 2019". See what happens if you try that without emptying stdin...)
To fully validate each required input, you must check for, and adequately handle, all three conditions set forth above, e.g.
for (;;) { /* loop continually until valid input received */
printf ("\n How many trains to operate on %s?"
" [1-5]: ", day);
int rtn = scanf ("%d", &trainsperday); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input.)\n", stderr);
else if (trainsperday < 0 || 5 < trainsperday) /* out of range */
fputs (" (error: trainsperday exceeds 5.)\n", stderr);
else /* good input, break input loop */
break;
}
With that, you can accomplish filling your trains schedule while handling all input failures or values out of range, while still allowing the user to cancel input at any point and providing a graceful exiting of your program. A reworking of your code could be:
#include <stdio.h>
/* my global variables - don't use them, declare in scope needed, but
* do #define any constants needed to avoid magic-numbers in your code
*/
#define MAXTRAIN 5
#define MAXDAY 15
/* simple function to empty stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void)
{
char day[MAXDAY];
int /* dailyTrainCount[5],*/ trainsperday,
trainTimeFrame[MAXTRAIN], i = 0;
fputs ("Welcome to TMS (TRAIN MANAGEMENT SYSTEM)\n\n"
" Enter day of Operation: ", stdout);
if (scanf ("%14s", day) != 1) { /* VALIDATE EVERY INPUT - check return */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
for (;;) { /* loop continually until valid input received */
printf ("\n How many trains to operate on %s?"
" [1-5]: ", day);
int rtn = scanf ("%d", &trainsperday); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input.)\n", stderr);
else if (trainsperday < 0 || 5 < trainsperday) /* out of range */
fputs (" (error: trainsperday exceeds 5.)\n", stderr);
else /* good input, break input loop */
break;
}
/* show train menu to user */
printf ("\n%d Train(s) will operate on %s\n\n"
" Available time frames\n"
" (1) - 7AM-10PM\n"
" (2) - 10AM-1PM\n"
" (3) - 1PM-4PM\n"
" (4) - 4PM-7PM\n\n", trainsperday, day);
while (i < trainsperday) { /* use while, not for, to handle errors */
printf (" Enter time frame for train #%d: ", i + 1);
int rtn = scanf ("%d", &trainTimeFrame[i]); /* save scanf return */
if (rtn == EOF) { /* handle EOF */
fputs ("(user canceled day input.)\n", stderr);
return 1;
}
empty_stdin(); /* empty remaining characters in stdin */
if (rtn < 1) /* if matching failure */
fputs (" (error: invalid integer input, valid input [1-4].)\n",
stderr);
else if (trainTimeFrame[i] < 0 || 4 < trainTimeFrame[i]) /* valid? */
fputs (" (error: invalid time frame, valid input [1-4].)\n",
stderr);
else /* good input, now increment i */
i++;
}
/* output train schedule */
printf ("\nTrain Schedule:\n\n");
for (i = 0; i < trainsperday; i++)
printf (" Train #%d is set to time frame #%d\n",
i + 1, trainTimeFrame[i]);
return 0;
}
(note: the use of a while loop instead of a for loop when filling the timeframes for each train. If a for is used and a failure occurs, you cannot recover. Using a while and only incrementing your counter on valid input provides a solution)
Now any time you write a program that takes input -- go try and break it. Enter invalid input on purpose to exercise the error handling of your code. If you break it, fix it and try again. After you have tried all the invalid input you can think of, have a cat step on the keyboard. If it passes all test, you can be reasonably sure you have covered the most likely misuse scenarios your users will provide.
Example Use/Output
With intentional invalid input for that purpose.
$ ./bin/trainsperday
Welcome to TMS (TRAIN MANAGEMENT SYSTEM)
Enter day of Operation: Tuesday April 23, 2019
How many trains to operate on Tuesday? [1-5]: Ten Trains on Tuesday
(error: invalid integer input.)
How many trains to operate on Tuesday? [1-5]: 7
(error: trainsperday exceeds 5.)
How many trains to operate on Tuesday? [1-5]: 3
3 Train(s) will operate on Tuesday
Available time frames
(1) - 7AM-10PM
(2) - 10AM-1PM
(3) - 1PM-4PM
(4) - 4PM-7PM
Enter time frame for train #1: 7AM-10PM
(error: invalid time frame, valid input [1-4].)
Enter time frame for train #1: 5
(error: invalid time frame, valid input [1-4].)
Enter time frame for train #1: 3
Enter time frame for train #2: 1
Enter time frame for train #3: 2
Train Schedule:
Train #1 is set to time frame #3
Train #2 is set to time frame #1
Train #3 is set to time frame #2
Look things over and let me know if you have further questions.
I'm a beginner in C, and I've got problem I can't figure out, and wasn't able to find a solution on other threads here.
I'm trying to read integers from a keyboard input/ txt file with the following code:
int grades[MAX_GRADES_LENGTH]={0}, histogram[HISTOGRAM_SIZE]={0};
int maxGradesHistogramBucket=0, median=0, gradesLength=0;
double avg=0.0;
int grade=0;
printf("Enter grades:\n");
while (scanf("%d",&grade) != EOF)
{
grades[gradesLength]=grade;
gradesLength=gradesLength+1;
}
I'm supposed to set these "grades" in the grades[] array and count the length of the array along the loop.
Somehow the loop is misbehaving, it seems that some inputs are ok with the loop, but for some inputs(most of them actually) the scanf() doesn't get the EOF, whether it's an actual end of file, or the ^D command in the terminal.
I've heard the scanf() is not the most reliable method to read data, but unfortunately I can't use anything else since we haven't learned any other method in class so we can only use scanf() on our homework.
I've tried to change the!= EOF with == 1 and its all the same.
for the input
100 0 90 10 80 20 70 30 60 40 50
for example it works fine.
but for the input:
0 50 100
the loop is infinite.
I'm using a macbook pro btw (if it matters).
Any ideas?
If you type a letter instead of a number, scanf() will return 0 (as in, "zero successfully converted numbers") and not EOF (as in, "there was no data left to read"). The correct test is to ensure that you got the expected number of values — in this case, 1:
while (scanf("%d", &grade) == 1)
If you need to know whether you got to EOF or got no result (but reading the rest of the line might clear the problem), then capture the return value from scanf():
int rc;
while ((rc = scanf("%d", &grade)) == 1)
{
}
if (rc != EOF)
…read the rest of the line, or at least the next character, before resuming the loop…
And, if you really want to, you could then write:
int rc;
while ((rc = scanf("%d", &grade)) != EOF)
{
if (rc == 1)
grades[gradesLength++] = grade;
else
{
printf("Discarding junk: ");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
putchar('\n');
if (c == EOF)
break;
}
}
The code in the else clause could plausibly be put into a function. It might also report the messages to standard error rather than standard output. It is courteous to let the user know what it was that you objected to. You could stop before newline with a different test (&& !isdigit(c) && c != '+' && c != '-', using isdigit() from <ctypes.h>). However, the user doesn't have a chance to re-edit the stuff they put after the letters, so you may be going to misinterpret their input. It is probably better just to throw away the rest of the line of input and let them start over again.
As chux noted, after reading a character that could be part of an integer, that character needs to be put back into the input stream. Therefore, if I were going to analyze the rest of the line and restart scanning at the first data that could actually be part of an integer, I'd consider using something like:
#include <ctype.h>
static inline int could_be_integer(int c)
{
return isdigit(c) || c == '+' || c == '-');
}
and then the else clause might be:
else
{
printf("Discarding junk: ");
int c;
while ((c = getchar()) != EOF && c != '\n' && !could_be_integer(c))
putchar(c);
putchar('\n');
if (could_be_integer(c))
ungetc(c, stdin);
else if (c == EOF)
break;
}
This gets messy, as you can see. Sometimes (as Weather Vane noted in a comment), it is easier to read a line with fgets() and then use sscanf() in a loop (see How to use sscanf() in a loop?). Be wary of suggestions about Using fflush(stdin); it isn't automatically wrong everywhere, but it won't work on a MacBook Pro under normal circumstances.
On the whole, simply ignoring the rest of the line of input is usually a better interface decision.
It works for me.
I enclosed your snippet thus:
#include <stdio.h>
#include <errno.h>
#define MAX_GRADES_LENGTH 20
#define HISTOGRAM_SIZE 20
main()
{
int grades[MAX_GRADES_LENGTH]={0}, histogram[HISTOGRAM_SIZE]={0};
int maxGradesHistogramBucket=0, median=0, gradesLength=0;
double avg=0.0;
int grade=0;
int i;
printf("Enter grades:\n");
while (scanf("%d",&grade) != EOF)
{
grades[gradesLength]=grade;
gradesLength=gradesLength+1;
}
if (errno)
perror("grade");
for (i = 0; i < gradesLength; ++i) {
printf("%d\n", grades[i]);
}
}
and ran it:
$ a.out
Enter grades:
100 0 90 10 80 20 70 30 60 40 50
100
0
90
10
80
20
70
30
60
40
50
$ a.out
Enter grades:
0 50 100
0
50
100
$
Perhaps you are looking in the wrong place. Maybe the bug is in your output routine?
Personally, if had to do this, given some ambiquity over what scanf returns when, and without rewriting it, then this small change is probably more reliable:
int i, r;
printf("Enter grades:\n");
while ((r = scanf("%d",&grade)) > 0)
I've been introduced to C this year as part of my degree, during which I have to write simple programs and test them to be idiot-proof by running them over and over again, putting nonsense variables in, etc. and I had an idea to write a program with the ability to restart itself without having to run the program again.
I've tried writing a program to perform this function (which turned out to be harder than I first thought) and I now have it working, albeit using a goto function that are frowned upon. Now the only problem I have is a while loop to check for nonsense input, that seems determined to run at least once ignoring a prompt for a valid input.
Please could someone give me an idea why this is happening? (My compiler is Dev-C++ 4.9.9.2)
int main (void)
{
mainprogram:
printf("\nPROGRAM START\n");
//code copied from an exam, to check that the program performs a function
//when ran through again
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
//this is to check that the program will take input when it is restarted
do {
switch (i%j) {
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
default:
i--;
k++;
break;
}
printf("i is %d k is %d\n",i,k);
} while (i>0);
//end of copied code
char prompt='y';
printf("\nRestart program?");
scanf("%c",&prompt);
while (prompt != 'y' && prompt != 'Y' && prompt != 'n' && prompt != 'N')
{
//this is the problem section, when entering nonsense input, the error messages
//display twice before pausing for input, and when restarted, the program does
//run again but displays the error messages once before pausing for input
printf("\nERROR: INVALID INPUT");
printf("\n\nRestart program?");
prompt='y';
scanf("%c",&prompt);
}
if (prompt == 'y' || prompt == 'Y')
{
goto mainprogram;
}
//
return 0;
}
while(1){ //parent
printf("\n\nRun program?");
scanf("%c",&prompt);
if (prompt == 'n' || prompt == `N`)
{
printf("\nEXITINT")
return 0;
}
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
switch (i%j)
{
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
break;
default:
i--;
k++;
break;
}
printf("i is %d k is %d\n",i,k);
} //end while parent
//end of copied code
There are a couple of ways one could restart a program, or more generally loop over some code. Of course, all of that can be done with gotos, but that makes the code hard to understand, so C has equivalent structures for dealing with the most common patterns:
execute code if and as long as condition holds
while (condition)
{
/* code */
}
This means before executing the code, the condition is checked. If the condition holds (its value is non-zero), the code is executed and then looped back to the top. This is equivalent to:
top_of_while:
if (!condition)
goto done;
/* code */
goto top_of_while:
done:
execute code and redo while condition holds
do
{
/* code */
} while (condition)
This means execute code first and then check for a condition. If the condition holds, executed the code again. This is equivalent to:
top_of_do_while:
/* code */
if (condition)
goto top_of_do_while;
iteration
for (initialization; condition; iteration)
{
/* code */
}
This is a kind of while loop that happens a lot, in which there is an initialization, followed by a while loop, which on the bottom changes a variable to form some sort of iteration. This is equivalent to:
initialization;
while (condition)
{
/* code */
iteration;
}
To restart a program, most likely you want the do-while loop, since for sure you know that the program has to execute once. However, by properly initialization the condition variable of a while loop, you can also ensure that the loop is always entered the first time. It's a matter of style and your liking.
Where to actually use goto
Many people would tell you to never use goto. This roots from the fact that overuse of goto had led to a great number of overly complicated programs, the so-called spaghetti code. The reason is that it's hard to build a mental model of a program where the execution can jump around to any other part.
However, gotos are actually very useful in C, without which error-handling becomes a huge pain in the ... neck. This useful usage of goto is for handling errors and cleanup, which are always in the bottom of the function. An example would be:
int *read_nums(int n)
{
int *array, i;
array = malloc(n * sizeof *array);
if (array == NULL)
goto exit_no_mem;
for (i = 0; i < n; ++i)
if (scanf("%d", &array[i]) != 1)
goto exit_bad_input;
return array;
exit_bad_input:
free(array);
exit_no_mem:
return NULL;
}
This way, the code is not cluttered (much) with error handling and cleanup is done very nicely based on how far the function has executed.
I just reformatted your code and indeed # herohuyongtao is right, the break; for case 2 has moved at the end of default which is not useful there.
But there's something really shocking in your code, is that you use a goto. Just remember that rule: WHENEVER YOU USE GOTO, THERE'S A BETTER WAY TO DO IT!
#include <stdio.h>
short int read_input (void) {
printf("\nPROGRAM START\n");
//code copied from an exam, to check that the program performs a function
//when ran through again
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
//this is to check that the program will take input when it is restarted
do {
switch (i%j) {
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
break; // break at the right spot
default:
i--;
k++;
}
printf("i is %d k is %d\n",i,k);
} while (i>0);
// getchar(); // could be inserted here (discards one char)
// fflush(stdin); // could also do the job (discards all remaining chars in buffer)
char prompt='y';
// here the design choice is to let the user input whatever
// and not updated the output until the right character is given
// which will hide how many wrong chars has been previously in the buffer.
printf("\nRestart program? ");
do {
prompt = getchar();
} while (prompt != 'y' && prompt != 'Y' && prompt != 'n' && prompt != 'N');
if (prompt == 'y' || prompt == 'Y')
return 1;
return 0;
}
int main() {
while (read_input() == 1);
return 0;
}
Now that the code is clean, for your exact problem, you will get into a carriage return problem. Basically, what you do is get input until \n is hit, but when you hit carriage return, you actually send CR + LF. So there's one character that never gets read in the input buffer, that you need to discard.
So you should first read that SO question that sums up very well your problem, and you can either:
add a lone getchar(); just after your //end of copied code comment,
or a fflush(stdin) can do the job (cf the SO Question to learn more about it) but has been designed about flushing output buffers, not input ones,
add a fseek(stdin,0,SEEK_END); which is a bit dirty (and non-portable) but works,
or change your conditions and your use of scanf to take into account that you're actually having more chars in the buffer.
In the code I gave you, I chose the most simplistic solution, which is to discard any wrong input without printing anything.