i just want to make sure that i read a positive value for x not sure whats wrong here
#include <stdio.h>
void main() {
unsigned x;
int i;
do {
printf("donner un nombre\n");
scanf("%u", &x);
} while (x <= 1);
/*
for (i = 0; i++; i < x) {
int prime = 0;
int x = 2;
do {
if (i % x == 0 ) {
prime = 1;
}
x++;
} while (prime == 0 && x < i);
if (prime == 0) {
printf("%i",i);
}
}
*/
}
Unsigned is bad choice
First at all you change unsigned type to integer int, that alloved you to scan negative numbers such as -1, -5,...
Wrong condition in loop
Also there is bug in do while loop, where you scan until the x <= 1 but number 1 is positive number, so you have to change it to x < 1
Final code will looks like this:
#include <stdio.h>
int main() {
int x;
do {
printf("donner un nombre\n");
scanf("%u", &x);
} while (x < 1);
// your code working with positive x
return 0;
}
Reading input, especially when it involves a numeric conversion is something done time and time again. It's worth taking the time to understand how to do it properly.
For starters, using scanf() for user-input can be done, but it is so full of pitfalls for the new C programmer that it is highly discouraged. This is due to the state that scanf() leaves your input buffer in both after good input (with potential additional characters and the '\n' left unread), and after invalid input (where all characters from the point of matching-failure forward are left unread). To avoid all of these pitfalls, simply read all input into a sufficiently sized buffer (character array) with fgets() and then convert the contents of the array as needed.
Why fgets() and a sufficiently sized buffer? fgets() reads an entire line of input (including the trialing '\n' produced by the user pressing Enter ensuring the input buffer (stdin in your case) is left in a proper state for your next read.
To convert the contents of the array to an integer value, strtol() provides the most robust diagnostics and error reporting in the event the user failed to enter a valid integer, but using sscanf() for the conversion at least provides a valid success/failed indication through its return.
(you couldn't have used scanf() correctly to begin with without checking the return)
How To Read User Input?
The approach is simple, and it virtually the same for any input needed. You simply:
loop continually prompting for input until a valid input is provided by the user (your read-loop),
read the user input into a sufficiently sized buffer (don't skimp on buffer size),
check if the user canceled input by generating a manual EOF by pressing Ctrl + d (or Ctrl + z on windows),
attempt conversion of the contents of your buffer as needed (to int in your case) checking the return of the conversion function used,
if the input is valid, check against any other constraints you have (positive int in your case)
if all conditions for good input met -- break out of the read-loop, otherwise,
handle the invalid input, and loop again.
No matter what you need to get from the user, you can use this approach. It's something you use so often, it makes sense to put it in a function you can call any time you need that type of input. For an int value, where you can provide an optional prompt to the user to be displayed, you can do something similar to:
...
#define MAXC 256 /* if you need a constant, #define one (or more) */
/* read integer value from 'fp', update value at 'val' address.
* returns 1 on success, 0 if user cancels input with ctrl+d (or
* ctrl+z on windows)
*/
int read_int (int *val, FILE *fp, const char *prompt)
{
char buf[MAXC]; /* buffer (char array) to hold line of input */
for (;;) { /* loop continually until valid input or user cancels */
if (prompt) { /* if prompt not NULL, display */
fputs (prompt, stdout);
}
if (!fgets (buf, MAXC, fp)) { /* read/validate line of input */
puts ("(user canceled input)");
return 0;
}
/* attempt conversion to int validating return/handle error */
if (sscanf (buf, "%d", val) != 1) {
fputs (" error: invalid integer input.\n", stderr);
}
else { /* otherwise, valid int received */
break; /* break input loop */
}
}
return 1; /* return success */
}
The function above will require the user to enter a valid integer, looping displaying the prompt and handling any input errors until the user does while still allowing the user to cancel input with a manual EOF. What it doesn't do is apply your constraint of requiring a positive value. This you add as an additional requirement where you call read_int() in your code.
For example, in main()1 since you need a positive value, you simply call the read_int() function in another continual loop until a positive value is returned (handling the manual EOF case as well). That can look like the following:
int main (void) { /* proper invocation of main unless on freestanding sys */
int x = 0;
for (;;) { /* loop continually prompting for input until x positive */
/* validate return of read_int() checking if user canceled */
if (read_int (&x, stdin, "donner un nombre: ") == 0) {
return 0; /* graceful exit on cancel -- not an error */
}
if (x > 0) { /* test x in acceptable range (positive) */
break; /* break read loop */
}
/* handle x outside of range */
fputs (" error: invalid input, must be postive integer.\n", stderr);
}
printf ("\npostivie x: %d\n", x);
}
(note: above you simply apply a continual loop again, looping until the int value is in the range of positive integers -- handle the 0 case however you want to consider it)
If you need to cover the entire range of unsigned, then change the function to be read_long() (and change the types appropriately). Then in main() you can #include <limits.h> and test if (0 < x && x <= UINT_MAX) for input in range.
Putting it altogether simply requires adding #include <stdio.h> at the top, e.g.
#include <stdio.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
/* read integer value from 'fp', update value at 'val' address.
* returns 1 on success, 0 if user cancels input with ctrl+d (or
* ctrl+z on windows)
*/
int read_int (int *val, FILE *fp, const char *prompt)
{
char buf[MAXC]; /* buffer (char array) to hold line of input */
for (;;) { /* loop continually until valid input or user cancels */
if (prompt) { /* if prompt not NULL, display */
fputs (prompt, stdout);
}
if (!fgets (buf, MAXC, fp)) { /* read/validate line of input */
puts ("(user canceled input)");
return 0;
}
/* attempt conversion to int validating return/handle error */
if (sscanf (buf, "%d", val) != 1) {
fputs (" error: invalid integer input.\n", stderr);
}
else { /* otherwise, valid int received */
break; /* break input loop */
}
}
return 1; /* return success */
}
int main (void) { /* proper invocation of main unless on freestanding sys */
int x = 0;
for (;;) { /* loop continually prompting for input until x positive */
/* validate return of read_int() checking if user canceled */
if (read_int (&x, stdin, "donner un nombre: ") == 0) {
return 0; /* graceful exit on cancel -- not an error */
}
if (x > 0) { /* test x in acceptable range (positive) */
break; /* break read loop */
}
/* handle x outside of range */
fputs (" error: invalid input, must be postive integer.\n", stderr);
}
printf ("\npostivie x: %d\n", x);
}
Example Use/Output
Now that you have written your input routine -- go try and break it, every way you can think of -- and make sure it handles all cases correctly. Then there will always be one or two corner-cases you didn't think about you will have to fix later.
Does the input routine work, requiring a positive value?
$ ./bin/read_int_function
donner un nombre: 0
error: invalid input, must be postive integer.
donner un nombre: banannas and pickles
error: invalid integer input.
donner un nombre: ten
error: invalid integer input.
donner un nombre: 10
postivie x: 10
What about handling the user canceling input?
$ ./bin/read_int_function
donner un nombre: foo
error: invalid integer input.
donner un nombre: (user canceled input)
Handling user-input is one area that gives new C programmers no end of grief. Best to take the time and think through it now, learning how to correctly approach it. Doing so will save you a great deal of time and frustration.
Let me know if you have further questions.
Footnotes:
1.) Unless you are programming in a freestanding environment (without the benefit of any OS), in a conforming implementation, the allowable declarations for main for are int main (void) and int main (int argc, char *argv[]) (which you will see written with the equivalent char **argv). See: C11 Standard - §5.1.2.2.1 Program startup(p1). See also: What should main() return in C and C++? In a freestanding environment, the name and type of the function called at program startup are implementation-defined. See: 5.1.2.1 Freestanding environment
If you are using Linux as a development environment, you can take a look at man 3 scanf. This will give you the function's documentation.
That being said, the function returns a number which indicates how many valid inputs were read.
Knowing that, you can read a positive integer with the following code to check for read status:
int rc;
unsigned int x;
rc = scanf("%u", &x);
if (rc == 1) {
// valid input data
}
Related
I have been stuck on this problem for hours and don't know what to do. The setup I have completes the guessing game and everything works fine except when the user guesses the same guess twice, it is not supposed to use a "try". I don't want to do anything too advanced, I understand that I have to save my last guess in a variable but do not know how to approach that. Thank you for the help.
srand(time(NULL));
int r = rand() %10 + 1 ;
int guess;
int tries = 0;
int lastguess = 0;
printf("Welcome user, please guess my number from 1 to 10\n"); ;
while(1){
scanf(" %d", &guess);
tries++;
if (guess == r) {
printf("Congratulations, you got it in %d tries\n", tries);
return 0;
}
else if( guess == lastguess) {
printf("You have already tried this value, try another");
tries - 1;
}
else if( guess < 11 && guess > r) {
guess = lastguess;
printf("Guess Lower!\n");
}
else if( guess > 0 && guess < r ){
guess = lastguess;
printf("Guess Higher!\n ");
}
else if( guess > 11) {
printf("The game will not continue since you chose\n");
printf("a number that is not between 1 and 10 :(\n");
return 0;
}
else if( guess < 1){
printf("The game will not continue since you chose\n");
printf("a number that is not bewtween 2 and 10 :(\n");
return 0;
}
}
Continuing from my comment, any time you need to keep track of whether (or how many times) a value within a range has been seen, you want a Frequency Array. Such as how many times is each letter used in this document, or has the user already guessed X?
A frequency array is simply an array with one element for each value in your range. The array is initialized all zero. When you read a value, you increment the index that corresponds to that value, e.g.
array[value]++;
So the array simply keeps track of how many times a given value is seen. In your case to prevent the user from entering the same number more than once and having it count as a valid try -- all you care about is whether array[value] == 0. If it does, then value has not been entered before. if (array[value] != 0) (or simply if (array[value])), you know the user has already provided that value, so it should not count as a valid try.
Think about it for a minute. It takes a bit to wrap your head around what is happening. You start with an array that has an elements that corresponds to each value in your range from 1-10 and each of the elements is initialized 0. So if the user enters 5, you will increment (add +1) the array index that corresponds to 5, e.g. array[5]++;. So array[5] now equals 1. If you check the index for the value the user enters each time against the value at that index in the array -- if it isn't 0, you know the user has already entered that value.
Frequency arrays work for many, many, many types of counting and uniqueness problems, so make sure you understand them before moving on. I have another write-up about them at How to remove duplicate char in string in C which may help if you are still unclear how they track the guesses entered by your user.
How to implement it in your code? Just add a int guessed[11] = {0}; array for your frequency array (the 11 instead of 10 just avoids having to map the indexes from a ones-based 1-10 to a zero-based 0-9 since all array indexes are zero-based in C. (plus the cost of 1 extra int element in the array to save having to guess - 1 each time makes sense).
So with an 11 element frequency array to capture your guesses from 1-10 that allows you to track your user's guess by incrementing guessed[guess]++; with each valid guess and before considering the guess valid, allows you to check if (guessed[guess] != 0) to know that the user has already used that guess.
Putting it altogether, (with a short aside first) you could do:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NGUESSES 10 /* if you need a constant, #define one (or more) */
#define MAXC 256 /* max number of characters for each entry */
(note: don't use Magic-Numbers in your code, if you need a constant... You now have a single convenient location at the top of your code where you can change the game to use 15, 20, ... whatever number of guesses, without having to pick through all array declarations, if(...) statements, loop limits, etc... to make the change)
Declare Your Frequency Array
int main (void) {
srand(time(NULL));
char buf[MAXC]; /* buffer (array) for all user-input */
int r = rand() % NGUESSES + 1,
guess,
guessed[NGUESSES + 1] = {0}, /* frequency array + 1 for range 1 - 10 */
tries = 0;
Your lastguess is no longer needed. The hours of trying to make that work for check if the user had already entered a guess has been replaced by your frequency array guessed[].
Read All User Input With a Line-Oriented Input Function
/* don't use Magic-Numbers in your code, use a constant */
printf ("\nWelcome user, please guess my number from 1 to %d\n", NGUESSES);
while (1) {
printf ("\nguess[%2d] : ", tries + 1); /* prompt for entry each time */
if (fgets (buf, MAXC, stdin) == NULL) { /* read every input into buffer/validate */
puts ("(user canceled input)"); /* ctrl+d, manual EOF is valid input */
return 0; /* allow user to cancel */
}
/* use sscanf to parse guess from buf - validate EVERY conversion */
if (sscanf (buf, "%d", &guess) != 1) { /* " " not needed, %d ignores whitespace */
fputs (" error: invalid integer input.\n", stderr);
continue;
}
Always take user-input with a line-oriented input function so what remains in your input buffer stdin does NOT depend on the scanf() conversion specifier used or whether a matching failure occurs. Try entering "bananas" as your input -- and you will quickly see why. Instead read with fgets() into an array and then use sscanf() to parse the values you need from the array (works just like scanf() but reads from your array instead of stdin). Why?
That way no matter what the user inputs, your while() loop doesn't spin off into an infinite loop if the user enters a non-integer value because --- you have read the entire line with fgets() so nothing will remain unread in stdin and regardless of whether the conversion succeeds or fails, stdin is ready for the next input attempt and not full of characters that scanf() left behind.
(with scanf() when a matching-failure occurs, character extraction from stdin ceases at that point so the bad input is left unread in stdin)
That's why you use fgets() (or POSIX getline()) to read every user-input.
Validate The Input Is In Range
if (guess < 1 || NGUESSES < guess) { /* validate guess in range */
fprintf (stderr, " error: %d not between 1 and %d\n", guess, NGUESSES);
continue;
}
Check Your Frequency Array To See If guess Already Entered
if (guessed[guess]) { /* check if freq array non-zero at guess index */
fprintf (stderr, " error: %d was a previous guess.\n", guess);
continue;
}
guessed[guess]++; /* increment index for guess indicating guess used */
tries++;
Only now do you know you have a valid input that will constitute a try.
Check Victory or Prompt "Higher!" or "Lower!"
if (guess == r) { /* check victory */
printf ("Congratulations, you got it in %d tries\n", tries);
return 0;
}
else if (r < guess) /* prompt for high guess */
puts (" Guess Lower!");
else /* prompt for low guess */
puts (" Guess Higher!");
}
}
That's it -- done. If you will notice the indentation - that is the complete program.
Example Use/Output
With intentional bad non-integer input and duplicate guesses and guesses out-of-range, you can exercise the program with:
$ ./bin/guess_1-10
Welcome user, please guess my number from 1 to 10
guess[ 1] : My dog has fleas and my cat has none :)
error: invalid integer input.
guess[ 1] : 6
Guess Higher!
guess[ 2] : 0
error: 0 not between 1 and 10
guess[ 2] : 11
error: 11 not between 1 and 10
guess[ 2] : 6
error: 6 was a previous guess.
guess[ 2] : 1
Guess Higher!
guess[ 3] : 3
Guess Higher!
guess[ 4] : 5
Guess Higher!
guess[ 5] : 7
Guess Higher!
guess[ 6] : 10
Guess Lower!
guess[ 7] : 8
Guess Higher!
guess[ 8] : 9 ... drum roll ... and
Congratulations, you got it in 8 tries
What if the user cancels input by generating a manual EOF with Ctrl + d (or Ctrl + z on windows)? A manual EOF is valid input for your program, so you should always respect a user's wish to cancel input by using it, e.g.
$ ./bin/guess_1-10
Welcome user, please guess my number from 1 to 10
guess[ 1] : 3
Guess Lower!
guess[ 2] : (user canceled input)
By using fgets(), you simply check the return, as you do with EVERY input function you use, and if it is NULL, you know the user generated a manual EOF, so simply exit at that point.
Full Code
The full code to make it easy to copy/paste/compile/test:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NGUESSES 10 /* if you need a constant, #define one (or more) */
#define MAXC 256 /* max number of characters for each entry */
int main (void) {
srand(time(NULL));
char buf[MAXC]; /* buffer (array) for all user-input */
int r = rand() % NGUESSES + 1,
guess,
guessed[NGUESSES + 1] = {0}, /* frequency array + 1 for range 1 - 10 */
tries = 0;
/* don't use Magic-Numbers in your code, use a constant */
printf ("\nWelcome user, please guess my number from 1 to %d\n", NGUESSES);
while (1) {
printf ("\nguess[%2d] : ", tries + 1); /* prompt for entry each time */
if (fgets (buf, MAXC, stdin) == NULL) { /* read every input into buffer/validate */
puts ("(user canceled input)"); /* ctrl+d, manual EOF is valid input */
return 0; /* allow user to cancel */
}
/* use sscanf to parse guess from buf - validate EVERY conversion */
if (sscanf (buf, "%d", &guess) != 1) { /* " " not needed, %d ignores whitespace */
fputs (" error: invalid integer input.\n", stderr);
continue;
}
if (guess < 1 || NGUESSES < guess) { /* validate guess in range */
fprintf (stderr, " error: %d not between 1 and %d\n", guess, NGUESSES);
continue;
}
if (guessed[guess]) { /* check if freq array non-zero at guess index */
fprintf (stderr, " error: %d was a previous guess.\n", guess);
continue;
}
guessed[guess]++; /* increment index for guess indicating guess used */
tries++;
if (guess == r) { /* check victory */
printf ("Congratulations, you got it in %d tries\n", tries);
return 0;
}
else if (r < guess) /* prompt for high guess */
puts (" Guess Lower!");
else /* prompt for low guess */
puts (" Guess Higher!");
}
}
Give it a go, let me know if it does what you need, and let me know if you have further questions. Make sure you understand theses concepts before moving on -- and I'm here to help if you are still stuck.
I want to write a C program to add the numbers given by the user as long as they want... can anyone fix this program?
I tried to use a do-while loop.
Any other suggestions to improve my code?
I am unable to end the loop.
#include <stdio.h>
int main()
{
int x=0, sum = 0, y=0, fu;
printf("first number you want to add:\n");
scanf("%d", &x);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
do
{
printf("do you want to add numbers further? \nEnter 0:Yes or 1:No: \n");
scanf("%d", &fu);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
while(fu>0);
sum=x;
printf("Sum of all integers = %d\n", sum);
return 0;
}
Ask for the 3rd and further numbers in an if and modify your while:
scanf("%d", &fu);
if(fu == 0) {
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
}
while(fu == 0);
Your prompt says:
Enter 0:Yes or 1:No:
so you need to continue that loop if 0 was entered:
while(fu == 0);
Also, you don't need to take another y after non-0 input.
The key to taking any input, either from the user, or from a file, is to validate every single input by checking the return. You cannot blindly use a variable holding input until you know whether the input succeeded or failed. Otherwise, if the input fails and you use a variable whose value is indeterminate, you invoke undefined behavior.
Also, if you are using a formatted input function such as scanf(), if a matching failure occurs, character extraction from stdin ceases at that point and the characters causing the failure are left in stdin -- unread, just waiting to bite you again on your next attempted input.
Instead, if you use a line-oriented input function such as fgets() or POSIX getline(), you read an entire line at a time. You can simply call sscanf() on the buffer filled by fgets() to convert a numeric input to an integer value. That way it does not matter if the conversion succeeds or fails, you do not leave anything unread in the input stream.
Just as you must validate every input, you so too must validate every conversion. Whether using sscanf() or strtol(), etc... a failure to validate every conversion will likely lead to undefined behavior when you fail to detect the conversion failure.
Another benefit of using fgets() or getline() is they read and store the '\n' from the user pressing Enter. So rather than having to prompt "do you want to add numbers further? \nEnter 0:Yes or 1:No: \n" and have to worry about yet another input and conversion -- you simply check if Enter was pressed on an empty line to know the user completed input (e.g. the first character in the buffer filed by fgets() is the '\n' character).
You also have to handle an invalid input correctly. What if the user enters "bananas" instead of a number?
Putting it altogether, you could do something similar to:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer (character array) to hold all user input */
int sum = 0, n = 0; /* sum and count of numbers */
puts ("press ENTER alone to exit:\n"); /* display instructions */
while (1) { /* loop continually */
int tmp; /* temporary int to add to sum */
/* prompt based on 1st or subsequent number */
fputs (n ? "next number : " : "first number : ", stdout);
/* read and validate input, break on EOF or empty line */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') {
puts ("---------------------");
break;
}
/* validate conversion to int */
if (sscanf (buf, "%d", &tmp) == 1) { /* on success */
sum += tmp; /* add to sum */
n += 1; /* increment count */
}
else /* handle error */
fputs (" error: invalid integer input.\n", stderr);
}
printf (" sum : %d\n", sum); /* output final sum */
}
Example Use/Output
$ ./bin/sum
press ENTER alone to exit:
first number : 10
next number : -20
next number : 30
next number : -40
next number : bananas
error: invalid integer input.
next number : 50
next number :
---------------------
sum : 30
There are several ways to approach this, and if you wanted the user to be able to enter more than one-number per-line, you could parse buf with strtol() to extract all values. (you can do the same with sscanf() using an offset from the beginning of the string and the characters consumed on each conversion from the "%n" specifier) Many ways to go.
Let me know if you have further questions.
I am curently learning C Programming in University and I got the task to write a program witch puts out the interception points with 0 / x-axis of any function ax^2+bx+c.
In order to make sure that the input is correct (int, float, etc.) I run the following while loop. Prior to that a is definded as a double.
printf("Input for a=");
while (scanf("%lf", &a) == 0)
{
fflush(stdin);
scanf("%lf", &a);
printf("Incorrect Input! New Input needed.\n");
printf("a=");
}
I am aware that the fflush(stdin) operator only clears the buffer when a second input function occurs and therefore the fflush inside the loop does not clear the buffer and therefore the condition of the loop is always true and thus I created an infinite loop.
My professor also forbids the use of goto, which is why I am here, because I can't come up with a reasonable solution that solves this problem.
I also tried and failed with:
do
{
printf("\nInput for a= ");
scanf("%lf", &a);
}
while (isdigit(a));
{
printf("Thank you.\n");
}
With this arrangement I get the failure notification: Expression c >= -1 && <= 255. I guess this has to do with false variable definition (double, digit) or such.
However my original question was whether there is an elegant solution to this problem or at least any solution.
Lukas, I'm still not 100% clear and your:
"Im am asking how to distinguish between all numbers and every other possible input for scanf."
scanf can provide conversion to a single given type based on the conversion specifier used, so you can't read both int and float with the same scanf statement and a single conversion specifier. (now you can read a line with, e.g. fgets and then use alternate sscanf statements and check the remaining characters to do that)
That said, I think I understand what you are asking and can answer both the fflush and read of a value questions.
To begin, when using scanf you must check the return and handle three-cases. (1) EOF, the user cancels input with a Ctrl+d (or Ctrl+z on windows); (2) a matching or input failure occurs resulting in a return of less than the number of conversion specifiers used; and finally (3) the good input case (where you impose any additional checks you have, e.g. positive, less than 100, etc..)
While fflush(stdin) is undefined behavior on most systems, there are a number of implementations that allow it. (primarily windows, but Linux allows it for seekable stream, e.g. a file redirected on stdin) Bottom line, it isn't portable without caveats, so it's best to provide a simple equivalent with getchar(), e.g.
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
As for scanf, as I mentioned in the comment, it is far easier to enclose the scanf call within an infinite loop which you only break when the input satisfies all your constraints. A simple example requiring integer input would be:
int getint (int *value, const char *prompt)
{
/* loop continually until good input or canceled */
for (;;) {
int rtn;
fputs (prompt, stdout); /* display prompt */
rtn = scanf ("%d", value);
if (rtn == EOF) { /* user generated manual EOF */
fputs ("<user canceled input>\n", stderr);
return 0;
}
empty_stdin(); /* all other cases - empty input buffer */
if (rtn == 1) /* good input, break */
break;
/* otherwise matching failure */
fputs (" error: invalid integer input.\n", stderr);
}
return *value; /* value also availale through pointer */
}
Putting it altogether in a simple example, you would have:
#include <stdio.h>
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int getint (int *value, const char *prompt)
{
/* loop continually until good input or canceled */
for (;;) {
int rtn;
fputs (prompt, stdout); /* display prompt */
rtn = scanf ("%d", value);
if (rtn == EOF) { /* user generated manual EOF */
fputs ("<user canceled input>\n", stderr);
return 0;
}
empty_stdin(); /* all other cases - empty input buffer */
if (rtn == 1) /* good input, break */
break;
/* otherwise matching failure */
fputs (" error: invalid integer input.\n", stderr);
}
return *value; /* value also availale through pointer */
}
int main (void) {
int v,
i = getint (&v, "enter integer value: ");
if (i)
printf ("\ninteger: %d\n", v);
return 0;
}
Example Use/Output
Where you can now do your best to break any input routine you write. If you find a problem, go fix it and try to break it again.
The code above allows for fairly robust input of any one given type of value, e.g.
$ ./bin/scanfint
enter integer value: no
error: invalid integer input.
enter integer value: apples, banannas, and pears
error: invalid integer input.
enter integer value: 21
integer: 21
Look things over and let me know if your question was slightly different, or if you have further questions about the answer.
How would I best take input in a program that asks the user to enter a student name separated by a space and then the student score, EX:
zach 85
Because of the null terminator, will there be two enters that i will have to account for? I'm already using two scanfs in my program.
int main()
{
const int row = 5;
const int col = 10;
int i;
char names[row][col];
int scores[row];
int total;
// Read names and scores from standard input into names and scores array
// Assume that each line has a first name and a score separated by a space
// Assume that there are five such lines in the input
// 10 points
for(int i = 0; i<row; i++)
{
printf("Enter student name: \n");
scanf("%s",&names);
scanf("%s", &scores);
}
// Print the names and scores
// The print out should look the same as the input source
// 10 points
for(int i = 0; i<row; i++)
{
printf( "%s %d \n", names[i] /*, scores[i] */ );
}
}
Your type for scores (int scores[row];) does not correspond to your attempt to read scores with scanf (scanf("%s", &scores);). The "%s" conversion specifier is for converting whitespace separated strings, not integers. The "%d" conversion specifier is provided for integer conversions.
Before looking at specifics. Any time you have a coding task of coordinating differing types of data as a single unit, (e.g. student each with a name (char*) and a score (int), you should be thinking about using a struct containing the name and score as members. That way there is only a single array of struct needed rather than trying to coordinate multiple arrays of differing types to contain the same information.
Also, don't skimp on buffer size for character arrays. You would rather be 10,000 characters too long than 1-character too short. If you think your maximum name is 10-16 character, use a 64-char (or larger) buffer to insure you can read the entire line of data - eliminating the chance that a few stray characters typed could result in characters remaining unread in stdin.
A simple stuct is all that is needed. You can add a typedef for convenience (to avoid having to type struct name_of_struct for each declaration or parameter), e.g.
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
Now you have a structure that contains your student name and score as a single unit rather than two arrays, one char and one int you have to deal with.[1]
All that remains is declaring an array of student_t for use in your code, e.g.
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
With the array of struct declared, you can turn to your input handling. A robust way of handling input is to loop continually, validating that you receive the input you expects on each iteration, handling any errors that arise (gracefully so that your code continues), and keeping track of the number of inputs made so that you can protect your array bounds and avoid invoking Undefined Behavior by writing beyond the end of your array.
You could begin your input loop, prompting and reading your line of input with fgets as mentioned in my comment. That has multiple advantages over attempting each input with scanf. Most notably because what remains unread in the input buffer (stdin here) doesn't depend on the conversion specifier used. The entire line (up to and including the trailing '\n') is extracted from the input buffer and place in buffer you give fgets to fill. You can also check if the user simply presses Enter which you can use to conveniently indicate end of input, e.g.
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
(note you can (and should) additionally check the string length of the buffer filled to (1) check that the last character read is '\n' ensuring the complete line was read; and (2) if the last char isn't '\n' checking whether the length is equal to the maximum length (-1) indicating that characters may be left unread. (that is left to you)
Now that you know you have a line of input and it's not empty, you can call sscanf to parse the line into the name and score for each student while handling any failure in conversion gracefully, e.g.
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
If you are paying attention, you can see one of the benefits of using the fgets and sscanf approach from a robustness standpoint. You get independent validations of (1) the read of user input; and (2) the separation (or parsing) of that input into the needed values. A failure in either case can be handled appropriately.
Putting all the pieces together into a short program, you could do the following:
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
for (int i = 0; i < n; i++) /* output stored names and values */
printf ("%-16s %3d\n", student[i].name, student[i].score);
}
Example Use/Output
When ever you write an input routine -- Go Try and Break It!. Intentionally enter invalid data. If your input routine breaks -- Go Fix It!. In the code as noted the only check left for you to implement and handle is input greater than COL number of characters (e.g. the cat steps on the keyboard). Exercise your input:
$ ./bin/studentnamescore
[note; press Enter alone to end input]
Enter student name: zach 85
Enter student name: the dummy that didn't pass
error: invalid input, conversion failed.
Enter student name: kevin 96
Enter student name: nick 56
Enter student name: martha88
error: invalid input, conversion failed.
Enter student name: martha 88
Enter student name: tim 77
array full - input complete.
zach 85
kevin 96
nick 56
martha 88
tim 77
While you can use two separate arrays, a single array of struct is a much better approach. Look things over and let me know if you have further questions.
footnotes:
Be aware that POSIX specifies that names ending with suffix _t are reserved for its use. (size_t, uint64_t, etc...) Also be aware you will see that suffix used in common practice. So check before you make up your own (but we no there is no POSIX student_t type).
You are almost there. However you have to make sure you do things cleanly Let's just do this step by step:
Step 1, get ONE name and ONE score
#include <stdio.h>
#define MAX_NAME_LENGTH 30
int main() {
char name[MAX_NAME_LENGTH+1]; /* an array of characters making up ONE name (+1 for terminating NUL char in case of max-length name) */
unsigned int score; /* a score */
scanf("%30s", name); /* scan a name (assuming NO spaces in the name)*/
/* also notice that name has no & in front of it because it already IS a pointer to the array name[MAX_NAME_LENGTH] */
scanf("%u", &score);
printf("%s scored %u in the test\n", name, score);
return 0;
}
(See it run at http://tpcg.io/jS3woS)
STEP 2 -- Iterate to get multiple scores
Now let's read in 5 pairs and then print out 5 pairs.
#include <stdio.h>
#define MAX_NAME_LENGTH 30
/* i called rows iterations here just to provide contrast */
/* you can call them ROWS if you want but it then creates a confusion about name length */
#define ITERATIONS 5
int main() {
char name[ITERATIONS][MAX_NAME_LENGTH+1]; /* an array of names where each name is MAX_NAME_LENGTH long (notice the order) */
unsigned int score[ITERATIONS]; /* score */
int i;
for(i = 0; i < ITERATIONS; i++ ) {
scanf("%30s", name[i]); /* scan a name (assuming NO spaces in the name)*/
/* notice that name[i] has no & in front of it because name[i] is the pointer to the i-th row */
scanf("%u", &score[i]);
}
for(i = 0; i < ITERATIONS; i++ ) {
printf("%s scored %u in the test\n", name[i], score[i]);
}
return 0;
}
See it in action here (http://tpcg.io/iTj4ag)
first look scores and names are is defined as arrays so
scanf("%s",names[i]);
scanf("%s", &scores[i]);
second scores are int so "%d" instead of "%s"
scanf("%s",names[i]);
scanf("%d", &scores[i]);
third, you've already defined int i; so the one in for loop is not really making any sense, do it only at a single place.
fourth, if your input names contains spaces scanf is not the right option
from manual pages of scanf
Each conversion specification in format begins with either the character '%' or the character sequence "%n$" (see below for the distinction) followed by:
· An optional decimal integer which specifies the maximum field width. Reading of characters stops either when this maximum is reached or when a non‐
matching character is found, whichever happens first. Most conversions discard initial white space characters (the exceptions are noted below), and
these discarded characters don't count toward the maximum field width. String input conversions store a terminating null byte ('\0') to mark the end
of the input; the maximum field width does not include this terminator.
few more points you might want to restrict the number of characters to be scanned during scanf to certain limit example "%20s"
I'm just getting introduced to C, and I was assigned to write a program that would mimic a self check out line at a grocery store. This involves me having to populate an array with the prices of grocery items based on user input, and add them up and copy them to a file.
the easiest way to populate an integer array is with a for loop. But would that be different for an array of type float?
would it look something like this? Or is this incorrect?
int size, i;
float items[size];
printf("How many items are you checking out today?");
scanf("%d", &size);
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", items[i]);
}
I'm new to this site so thanks in advance
I would recommend always initializing variables as you declare them to prevent "garbage" values by accident. Also, I don't really recommend pre-declaring your loop counters. You see it in a lot of old code (it used to be required due to compiler limitations), but now I just think it's code noise. It would look like this:
for (int i = 0; i < size; i++) {
// stuff
}
Also, your code has a big problem. You're using what's known as a variable-size array, and they are not a good idea. You generally want to either declare the array size at compile time, or dynamically allocate the space for the array using malloc.
Going back to the initalization, though, this is how you would set every element in a stack-allocated array on declaration:
#define SIZE 4
int main(void)
{
float items[SIZE] = { 0 };
}
If you dynamically allocate the array, I recommend using calloc or memset to set the array elements to a default value for the same reason.
To answer your question about populating the array, yes, there is no difference as to how you would actually go about doing it. A for loop works just fine in both cases. Just remember to check the return value of scanf.
As has been correctly pointed out, you cannot declare float items[size]; until size has been validly initialized to a positive integer value. Your attempt to declare items before size contains a value invokes Undefined Behavior due to your attempt to access an uninitialized value. (the valid operation of your code is over at that point and it could do anything from appearing to run correctly to StackOverflow, or SegFaulting)
Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin here). This is especially true when taking input with scanf (or family) due to the way scanf handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left unread in the input buffer -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)
(this is one of the primary reason a line-oriented function such as fgets is recommended for taking user input)
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 canceling input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10);
(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, etc..); and
otherwise, you must handle the matching or input failure and you must account for every character that may be left in your input buffer. (generally you will scan forward in the input buffer until a '\n' or EOF is found discarding any extraneous characters that remain)
If you do your job, you can successfully use scanf as needed.
Next, a general caution do not using floating point for currency (people get real mad when you start losing money due to rounding errors) While it is fine for your example program -- just understand, in a real currency handling program, you would handle currency as an unsigned value multiplied by 100 (or whatever is required) to insure all amounts can be represented exactly.
Putting the scanf requirements together, you could do something like the following safely:
#include <stdio.h>
/* function to empty extraneous characters from stdin
* (like the '\n' generated by pressing [Enter])
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int size = 0, i;
float total = 0.0;
for (;;) { /* loop continually until valid size entered */
int rtn;
printf ("How many items are you checking out today?: ");
rtn = scanf ("%d", &size);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && size > 0) { /* valid int received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
float items[size]; /* declare VLA of size floats */
for (i = 0; i < size; i++) {
items[i] = 0.0; /* initialize each (or memset VLA) */
for (;;) { /* loop continually until valid item price entered */
int rtn;
printf (" price of item[%2d]: ", i + 1); /* prompt for price */
rtn = scanf ("%f", &items[i]);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && items[i] > 0) { /* valid price received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
total += items[i];
}
printf ("\ntotal (%d items): $%.2f\n", size, total);
}
Example Use/Output
(shown with intentional errors in entry)
$ ./bin/checkout
How many items are you checking out today?: what?
error: invalid input.
How many items are you checking out today?: 4
price of item[ 1]: free?
error: invalid input.
price of item[ 1]: 1.25
price of item[ 2]: 3.50
price of item[ 3]: discount?
error: invalid input.
price of item[ 3]: 2.25
price of item[ 4]: 3
total (4 items): $10.00
Look things over and let me know if you have further questions.
There is no difference in usage of arrays in terms of usage. But there are few changes required in your code.
#define MAX_SIZE (10)
int size=0, i=0; //It is always better to initialize the variables.
float items[MAX_SIZE] = {0.0f}; //Automatically the entire array will be initialized to zero.
printf("How many items are you checking out today?");
scanf("%d", &size);
if(size > MAX_SIZE)
size = MAX_SIZE;
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", &items[i]); //You need to pass address of variable to scanf
}
There are other ways to implement your code to handle array size. This is one of the way.