How to not count the same number twice in a guessing game - c

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.

Related

Trying to read a positive value in c

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
}

I want to write a C program to add an arbitrary list of numbers given by user...can anyone fix this program?

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.

Is populating an integer array the same as populating a float one?

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.

scanf test failing inside a function in C

I'm trying to do a program with a simple game for a user to guess the number. My code is below:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 30
#define TRYING 5
void guessnumber(int, int, int *);
int main(void) {
int mytry = 1;
guessnumber(MAX, TRYING, &mytry);
if (mytry <= TRYING)
printf("Congratulations! You got it right in %d tries\n", mytry);
else
printf("Unfortunately you could not guess the number in the number of tries predefined\n");
printf("End\n");
return EXIT_SUCCESS;
}
void guessnumber(int _n, int _m, int *_mytry) {
srandom(time(NULL));
int generated = 0, mynum = 0, test = 0;
generated = rand() % (_n + 1);
printf("Welcome to \"Guess the number\" \n");
printf("A number between 0 and %d was generated\n", _n);
printf("Guess the number:\n");
while (*_mytry <= TRYING) {
test = scanf(" %d", &mynum);
if (test != 1 || mynum < 0 || mynum > MAX)
printf("ERROR: please enter a valid number \n");
else
if (mynum > generated)
printf("Wrong! The number your trying to guess is smaller\n");
else
if (mynum < generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry = *_mytry + 1;
}
}
Okay, now the program is working pretty ok except for one thing: the scanf test.
It works if I try to enter a number out of my range (negative or above my upper limit) but it fails if I for example try to enter a letter. What it does is that it prints the message of error _m times and then it prints "Unfortunately you could not guess the number in the number of tries predefined" and "End".
What am I doing wrong and how can I fix this?
In case, a character is entered, you're trying to detect it correctly
if(test!=1 ......
but you took no action to correct it.
To elaborate, once a character is inputted, it causes a matching failure. So the input is not consumed and the loop falls back to the genesis position, only the loop counter is increased. Now, the previous input being unconsumed, is fed again to the scanf() causing failure once again.
This way, the loop continues, until the loop condition is false. Also, for every hit to scanf(), as unconsumed data is already present in the input buffer, no new prompt is given.
Solution: You need to clean the input buffer of existing contents when you face a failure. You can do something like
while ((c = getchar()) != '\n' && c != EOF);
to clean the buffer off existing contents.
When you enter a letter, scanf() leaves the letter in the input stream since it does not match the %d conversion specifier. The simplest thing to do is use getchar() to remove the unwanted character:
if (test != 1) {
getchar();
}
A better solution would be to use fgets() to get a line of input, and sscanf() to parse the input:
char buffer[100];
while (*_mytry<=TRYING)
{
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()");
exit(EXIT_FAILURE);
}
test=sscanf(buffer, "%d", &mynum);
if(test!=1 || mynum<0 || mynum>MAX)
printf ("ERROR: please enter a valid number \n");
else if(mynum>generated)
printf("Wrong! The number your trying to guess is smaller\n");
else if(mynum<generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry=*_mytry+1;
}
In the above code, note that the leading space has been removed from the format string. A leading space in a format string causes scanf() to skip leading whitespaces, including newlines. This is useful when the first conversion specifier is %c, for example, because any previous input may have left a newline behind. But, the %d conversion specifier (and most other conversion specifiers) already skips leading whitespace, so it is not needed here.
Additionally, your code has srandom() instead of srand(); and the call to srand() should be made only once, and probably should be at the beginning of main(). And, identifiers with leading underscores are reserved in C, so you should change the names _m, _n, and _mytry.

Stop a for loop when user is finished entering input in c

First of all, thank you for the assist!
I'm new to the C language (and programming in general) and I'm trying to write a program wherein the user inputs data points. The data points are then saved in an array where they can then be manipulated.
Where I am stuck: I want the user to be able to input (almost) any number of points, then use a 'keyword' of sorts to signal the end of data entry. In this case, the user would type 'done'.
Here's what I have so far:
#include <stdio.h>
#include <string.h>
int main(void) {
printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");
double data[1048];
int i, count;
for (i = 1; ;i++) {
printf("Data[%i]: ", i);
scanf("%lf", &data[i]);
if (data[i] == 'done') {
break;
} else {
count++;
}
}
}
I've tried 'return 1;' and 'break;'. Each time, the program works well until the 'keyword' is entered, at which point I get:
Data[8]: Data[9]: ... Data[1120]: Data[1Segmentation fault 11
The only time it works is if I have it break when the user inputs a particular number (like -1 or 0). But that doesn't quite work for the user since they might have to enter those numbers as data points.
Sorry for the long post, but I appreciate the help!
You have received a number of good answers to your question, and there are several more ways to take input of doubles and stop on "done". Since you are learning C, always, ALWAYS (in case it wasn't clear), check the return of scanf to validate the number of conversions you expected actually took place.[1] (this also provides your way to end input on "done" (or any non-double entered causing scanf to return less than 1)
As noted in the comment, arrays are zero based in C. When you are taking input, you will want to use count as your array-index, rather than i (in this case if you exit the read on each failure -- it doesn't matter, but you could just as easily prompt again for additional input and increment count only on a successful return from scanf) Back to your question. If you set up your read loop to continually loop until there is a scanf failure, you can make use of a temporary variable to initially capture the input value, and only assign the value to your array and increment your index on success. e.g. (with a constant MAXD = 1048)
for (;;) { /* loop until scanf input fails (with 'done') */
double tmp; /* block scope declarations are fine */
printf (" data[%4d]: ", count);
if (count < MAXD && scanf(" %lf", &tmp) == 1)
data[count++] = tmp;
else
break;
}
(you can even move a copy of the prompt above the loop, and move the one above after the if (....) {...} to eliminate the prompt when the array limit (MAXD) is reached -- that's left as an exercise)
In the example above you have 2 conditions you enforce before storing a value. (1) you limit the number of values your user can store to MAXD, and (2) you only store a value if a valid conversion to double takes place in scanf. You leave the loop if either of the conditions fails (which if you enter "done" as a double-value, it will).
Putting the pieces together and dropping a few additional tips in the comments, you could test with something like the following:
#include <stdio.h>
enum { MAXD = 1048 }; /* declare constants instead of using magic numbers */
int main (void) {
double data[MAXD] = {0}; /* in ISO C declarations come before code */
int i, count = 0; /* initializing variable saves debug time */
printf ("\n Welcome! \n\n Please enter each data point. "
"Enter 'done' when finished.\n\n");
for (;;) { /* loop until scanf input fails (with 'done') */
double tmp; /* block scope declarations are fine */
printf (" data[%4d]: ", count);
if (count < MAXD && scanf(" %lf", &tmp) == 1)
data[count++] = tmp;
else
break;
}
printf ("\n %d values entered:\n\n", count);
for (i = 0; i < count; i++)
printf (" data[%4d] : %.2lf\n", i, data[i]);
return 0; /* main() is type 'int' and returns a value */
}
Example Use/Output
$ ./bin/scanfdoubles
Welcome!
Please enter each data point. Enter 'done' when finished.
data[ 0]: 1.1
data[ 1]: 1.2
data[ 2]: 1.3
data[ 3]: 1.4
data[ 4]: 1.5
data[ 5]: 1.6
data[ 6]: done
6 values entered:
data[ 0] : 1.10
data[ 1] : 1.20
data[ 2] : 1.30
data[ 3] : 1.40
data[ 4] : 1.50
data[ 5] : 1.60
Look things over and let me know if you have any questions.
footnotes:
1. while you can use scanf to take user-input in C, you are better off using a line-oriented function (like fgets) and then parsing the complete line (with, e.g. sscanf). The allows you to both (1) validate the read (e.g. the return of fgets) and then (2) separately validate the value entered by the user. This decoupling of your read, and your parsing has many advantages.
No element of data[] will ever be 'done' (they're floats). If you want to scanf() directly, you'll need to choose a double value that ends the sequence (commonly zero or -1 or something). If that won't work, you can either use something like:
Use fgets() to pull a string, then strncmp() to check for the terminating value and sscanf() to pull out the double, or:
Have the user use Ctrl-D to terminate and check the scan value for EOF.
Oh, and strictly speaking you have an upper limit of entries. You should check i to make sure that you don't exceed that. Never assume your input won't exceed boundaries. sizeof() on a statically-allocated variable or some #defined macro to track that.
Your data is of type double. It can't scan a literal "done".
Instead use EOF for checking end of input.
while(scanf("%lf",&data[i]) != EOF) {
...
}
Another way:
while(scanf("%lf",&data[i]) == 1) {
...
}
Another thing, initialize count to zero, i.e. count = 0;
Bottom line: don't use scanf.
Use something like
char inputline[100];
i = 0;
while(fgets(inputline, sizeof(inputline), stdin) != NULL) {
if(strncmp(inputline, "done", 4) == 0) break;
data[i++] = atof(inputline);
}
scanf is hard enough to use even when all your inputs are the numbers you expect. If the input might be either a number or the word "done", scanf will never work. But reading a line of text, as here, is generally easier and more flexible.
P.S. You also have to worry about the possibility that the user enters more than 1048 numbers.
For your task the loop of gathering input should control not only keyword, but also number if inputs. I suggest to do this as follows:
#include <stdio.h>
#include <string.h>
#define NUM_OF_DATA 1048
int main(void)
{
printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n");
double data[NUM_OF_DATA];
int i; // counter of entered numbers
char str[5] = { 0 }; // string to read 'done' or other word from input
for (i = 0; i < NUM_OF_DATA; i++) // the first index of data in array is 0 (the last NUM_OF_DATA-1)
{
printf("Data[%i]: ", i);
if (1 == scanf("%lf", &data[i])) // if number was successfully read
continue; // go to next iteration
// if some problem was with reading a loat number
// read the string
scanf("%4s", str); // read not more than 4 characters from input
if ( strcmp(str, "done") == 0)
{
break; // stop input if 'done' was entered
}
// clean input buffer before next input
while (getchar() != '\n');
// correct counter in case of wrong input
i--;
}
// output the number of correct inputs
printf("%d numbers were entered.\n", i);
// do something with data
// taking in account, that i is not index of the last element,
// but the number of elements (indexes are 0 ... i-1)
// ...
return 0;
}
This for loop stops in two cases:
1) when data array is full,
2) when 'done' without quotes entered.
Additional feature is skipping of incorrect input (try how it works).

Resources