How to read a yes / no response from the user? - c

Taking a beginning programming class and learning C. This is the first significant obstacle I've run into, was at it for 6+ hours yesterday and couldn't find a solution, so I gave up and decided to ask for help.
Assignment is to build a number guessing game (haven't yet gotten to the random number generation part) with a loop based on a boolean, where the user is prompted after a correct guess to play again and answer y or n.
I've tried a bunch of stuff, and the loop either terminates regardless of the option chosen (which is what it does in its current state), or loops endlessly regardless, and I'm not sure what I'm doing wrong.
Here's my code. Thanks in advance.
#include <stdio.h>
#include <stdbool.h>
int main()
{
int num = 5; /* temp placeholder for testing */
int guess;
char* response;
bool running = true;
while (running)
{
printf("Guess a number: ");
scanf("%d", &guess);
if (guess < num)
{
printf("That's too low.\n\n");
}
else if (guess > num)
{
printf("That's too high.\n\n");
}
else
{
printf("That is correct.\n\n");
guess = 0;
printf("Play again? (y/n): ");
scanf(" %c", response);
printf("Response: [%s]", response);
printf("\n");
if (response == "y")
{
running = true;
}
else
{
running = false;
}
}
}
return 0;
}

You are confusing strings with a single character. Here you have response declared as a pointer to a char.
char* response;
change that to
char response;
change
scanf(" %c", response);
to - this is passing in the address of the one character variable. %c accepts one character.
scanf(" %c", &response);
Change
if (response == "y")
to
if (response == 'y')
string literals use double-quotes. Also if you actually wanted to compare strings, that is not the correct way either, and you should look at the strcmp() function.

The first issue to be aware of is that == is not defined for strings (or other array types) - response == "y" doesn't work like you'd expect1.
To compare strings, you need to use the strcmp library function:
if ( strcmp( response, "y" ) == 0 ) // strcmp returns 0 if the strings match
{
// do something
}
The second issue is that you're confusing your types. You declare response as a char *, meaning that it's meant to store the address of another char object, not a character value. However, you never initialize it to point to a char object - its value is indeterminate, and it's pointing somewhere random. When you call scanf with it, you're writing the input value to some random location which in your case just happens to be writable and not "important" (in the sense that it doesn't cause your code to crash immediately).
You most likely want to do it the way OldProgrammer showed - change the type from char * to char, change the scanf call to use &response, and change the test to response == 'y' (single instead of double quotes).
Alternately, you can treat response as a string - in that case, you'd declare it as an array of char, large enough to hold your response plus the string terminator - in this case, 2 elements:
char response[2]; // 'y' or 'n' plus a string terminator
Instead of using scanf (which is prone to buffer overflows if you aren't careful), we'd use fgets instead:
if ( !fgets( response, sizeof response, stdin ) )
// input operation failed, handle as appropriate
else
// process response
and then as I said above, you'd use strcmp for the comparison:
if ( strcmp( response, "y" ) == 0 )
// process yes response
else
// process no response
Interestingly, you got the format string right for reading a single character (most new C programmers don"t). Unlike most conversion specifiers, %c won't skip over any leading whitespace, and more often than not people wind up reading stray newlines rather than the input they intend. Putting the blank before it in " %c" tells scanf to skip over any leading whitespace.
What it's actually doing is comparing the address value stored in response to the address of the first character of the string literal "y". The only way that expression will ever be true is if response stores the address of the literal "y".

Related

Loop back to question instead of printing out "Try again"? (C)

this is my first day coding and I'm trying to see how the code work, how can I change Y/N to Yes/No and loop back questions if the answer is not Yes instead of printing out "Try again?" or make it better?
int main(void)
{
char answer;
int name;
printf("Enter Username: \n");
scanf("%s",&name);
printf("Is your username \"%s\"? Enter Y or N\n", &name);
while (scanf(" %c", &answer) == 1 && answer == 'Y')
{
char answer2;
Sleep(1000);
printf("Username confirmed\n");
Sleep(2000);
int pass;
printf("Enter your password: \n");
scanf("%s",&pass);
printf("Is your password \"%s\"? Enter Y or N\n", &pass);
while (scanf(" %c", &answer2) == 1 && answer2 == 'Y')
{
printf("Success!");
Sleep(1000);
exit;
}
printf("Try again..\n");
Sleep(1000);
exit;
}
Sleep(1000);
printf("Try again..\n");
Sleep(1000);
exit;
return 0;
}
My best advice, as Weather Vane told you in the comment section, is to try to compile your code, before making changes: you'll find out that it won't even compile, since there are some errors (https://godbolt.org/z/MdahM4sT8).
You should debug and try to figure out why it doesn't work, fix it, then move to the new "features".
You probably want to change the while() condition and read a sequence of characters (array of char) instead of a single char.
char answer[4];
while (scanf("%3s", answer) > 0 && strcmp(answer, "Yes") == 0)
char answer[4]; creates an array of characters (which can be called a string, even though C doesn't have that abstraction) of size 4.
You might want to know why we need a size of 4, since the longer answer we can get is "Yes". Well, a string in C is a sequence of characters (char), ending with a special character, called terminating character '\0'.
Therefore, when you read a string with scanf(), you need to reserve some space for the terminating character aswell, and scanf() will add it when reading a space or carriage return after your input string. So if you enter "Yes" and then press the Return key, your answer[4] buffer will look like: {'Y', 'e', 's', '\0'}.
%3s tells scanf() to read only the first 3 characters of the string (prevents security issues like buffer overflow).
int scanf(const char *format, ...)
Description
Reads formatted input from stdin.
Return Value
On success, the function returns the number of items of the argument list successfully read. If a reading error happens or the end-of-file is reached while reading, the proper indicator is set (feof or ferror) and, if either happens before any data could be successfully read, EOF is returned.
Once you've read that string, you can use strcmp(), a function defined in `<string.h> header, that takes in 2 string parameters, and returns:
a negative value if the first precedes alphabetically the second;
0 if they're equal;
a positive integer if the second precedes alphabetically the second;
Therefore you can check if strcmp(answer, "Yes") == 0 to know if you've read Yes, and

Generating a dice game - C Programming

I'm following a tutorial on youtube and was doing a dice generator.
It basically print out 3 dice result and sum out the dice result.
After which, the user will look at the sum, and based on the sum, the user going to guess whether the next roll is going to be higher,lower, or the same.
Below is my code, suppose, when I typed 'yes', it should be doing the code inside the if statement. However, it went straight to the else statement. Can someone please tell me what's wrong?
int answer;
int guess;
int diceRoll4 = 0;
printf("Would you like to guess your next dice? Y/N \n");
scanf(" %c", &answer);
if (answer == 'yes' ){
printf("What is your guess?\n");
printf("please key in your number \n");
scanf(" %d", &guess);
if (guess > diceRoll4 ){
printf(" You got it wrong, too high!");
}
else if (guess < diceRoll4){
printf(" You got it wrong, too low!");
}
else {
printf("You got it right");
}
}
else{
printf("Thanks for playing");
}
First of all, answer should be an array of chars in order to hold a string. Change
int answer;
to
char answer[10]; //Or any other reasonable size
Secondly, since you want to scan a string and not a character, change
scanf(" %c", &answer);
to
scanf("%9s", answer);
The 9 will scan a maximum of 9 characters (+1 for the NUL-terminator at the end), thus preventing buffer overflows.
I've removed & as %s expects a char* while &answer will give a char(*)[10]. Name of an array gets converted into a pointer to its first element char*, exactly what %s expects. The above scanf is thus equivalent to
scanf("%9s", &answer[0]);
Thirdly, comparing two strings using == compares pointers and not the actual content in them. Use strcmp from string.h instead. It returns 0 when both its arguments hold the same content. Change
if (answer == 'yes' ){
to
if (strcmp(answer, "yes") == 0){
Double quotes are used to denote a NUL-terminated string(char*), which is exactly what strcmp expects, while single quotes, as in your code, is a multi-character literal whose value is implementation-defined.
'yes' is a multi-byte character whose behaviour is implementation-defined.
What you probably want is to read and compare a single char:
if (answer == 'y' ){
or read a whole string and compare:
char answer[128];
scanf("%s", answer);
if ( strcmp(answer,"yes") == 0 ){
...
}
Notice that I changed the type of answer and used %s to read a string.
If you do not want to read in a string, but only a single char where the user can answer either Y or N, you should change int answer; to char answer;. You can then go on using your original scanf()-call. You will still need to change
if (answer == 'yes')
to
if (answer == 'Y')
If you want the user to either type in y or Y you could user toupper() from ctype.h and change your if-condition to if (toupper(answer) == 'Y').
To test the equality you have to use strcmp. If the returning value is 0 it means that they are equal.
if (strcmp(answer, "yes") == 0) {
// ...
} else {
// ...
}
Notes:
Using just answer == 'yes' it test the equality of pointers not value. This is the reason why enters only in else.
Because answer is int you have to change to an array
char answer[15]
As #Sathya mentioned you are reading just a char %c for reading a string you have to use %s
scanf("%s", answer);
Instead of 'yes' which is multi-character character constant change to "yes" that is an array of char with \0 at the end, more informations here.
this line:
if (answer == 'yes' ){
has several problems.
1) the definition of 'answer' is 'int' but the scanf is inputting a single character
2) answer could be compared with 'y' or 'n' but not to a array of char.
3) since the scanf only input a single char
and you/the user input 'yes',
only the first character was consumed,
so the 'es' are still in the input buffer
4) note the the single character could be anything, except white space.
the leading space in the format string would consume any white space.
so the user could input say 'y' or 'Y'
these are different characters
however, using the toupper() macro from ctypes.h
would mean only a 'Y' would need to be compared
5) if you decide to read a string,
then 'answer' needs to be a character array,
say: char answer[10];
and the scanf needs to have a max length modifier
on the associated "%s" input/conversion parameter
so as to avoid the user overflowing the input buffer
and the comparison would be via the strcmp() function
6) always check the returned value (not the parameter value)
from scanf to assure the operation was successful
7) diceRoll4 and guess can never be a negative number
so the variable definitions should be unsigned
and the associated scanf() for guess should use
something like "%u"
8) on the printf() format strings, always end them with '\n'
so the sting will be immediately displayed to the user,
otherwise, they will only be displayed
when a input statement is executed or the program exits

How Does Char Variable Work in C

What I am trying to accomplish is prompting the user with the question of do they want to run the program again. They either type y or n. If y, it reruns the program. If no, it stops the program. Anything other than those two will prompt an error and ask the question again. I'm used to C# where strings are not complicated, but in C, I guess there technically isn't strings, so we have to use either char arrays or char pointers. I've tried both, none that work that way I want, but I'm probably the problem. This is what I have.
char answer[1] = "a";
while (strcmp(answer, "y") != 0 || strcmp(answer, "n") != 0)
{
printf ("\n\nWould you like to run the program again? Type y or n. Then, hit Enter.");
scanf ("%c", answer);
if (strcmp(answer, "y") == 0)
{
main();
}
else if (strcmp(answer, "n") == 0)
{
continue;
}
else
{
printf ("\nERROR: Invalid input was provided. Your answer must be either y or n. Hit Enter to continue.");
F = getchar();
while ((getchar()) != F && EOF != '\n');
}
}
I have other while loops similar to this that work as expected, but use a float. So I'm assuming the problem is me using char here. What happens right now is that it doesn't even prompt the user for the question. It just asks the question and shows the error right afterwards. I'm sure there are other things wrong with this code, but since I can't get the prompt to work, I cannot test the rest of it yet.
I suggest using a light weight getchar() instead of the heavy scanf.
#include <stdio.h>
int c; /* Note getchar returns int because it must handle EOF as well. */
for (;;) {
printf ("Enter y or n\n");
c = getchar();
switch (c) {
case 'y': ...
break;
case 'n': ...
break:
case EOF:
exit(0);
}
}
"a" is a string literal == char id[2]={'a','\0'} //Strings are
char arrays terminated by zero, in C
'a' is a char literal
strcmp is just "compare each char in two strings, until you hit '\0'"
scanf ("%c", ___); expect an address to write to as the second
argument. Functions in C cannot modify their arguments (they don't
have access to them--they get their own local copy) unless they have
a memory address. You need to put &answer in there.
Jens has already basically answered the question, you most likely want to use getchar so that you can detect EOF easily. Unlike scanf("%c",...), getchar will not skip spaces, and I believe both versions will leave you with the unprocessed rest of the input line (a newline character ('\n') at least) after each getchar. You might want to something like
int dump;
while((dump=getchar())!='\n' && dump!=EOF) {};
So that you discard the rest of the line once you've read your first character of it.
Otherwise, the next getchar will get the next unprocessed character of the same line. ('\n' if the line was a single letter).
Here is one way to do it. It is by no means the only way to do it, but I think it accomplishes what you want. You should not call the main function recursively.
#include <stdio.h>
#include <stdlib.h>
void run_program()
{
printf("program was run.");
}
int main() {
char answer[2] = "y\0";
int dump;
do {
if (answer[0] == 'y')
{
run_program(); /* Not main, don't call main recursively. */
}
printf ("\n\nWould you like to run the program again? Type y or n. Then, hit Enter.\n");
scanf ("%1s", answer);
/* Dump all other characters on the input buffer to
prevent continuous reading old characters if a user
types more than one, as suggested by ThorX89. */
while((dump=getchar())!='\n' && dump!=EOF);
if (answer[0] != 'n' && answer[0] != 'y')
{
printf ("Please enter either y or n\n");
}
} while (answer[0] != 'n');
return 0;
}
Using %s instead of %c, reads in the new line so that the new line character is not in the stdin buffer which would become answer then next time scanf was called.
The run_program function is just a function where you would put your program's logic. You can call it whatever you want. I did this to separate out the menu logic from the logic of the actual program.
Well, you are comparing two strings instead of characters.
If you want to compare two character you have to follow this syntax:
char c;
scanf("%c",&c);
if(c == 'y')
//do something
else
//do nothing

How to ensure user-input data is an integer

I am new to C, but I know C#. In C#, I could use TryParse to ensure that the user typed in the correct datatype.
Here is the basic code I have for C:
int shallowDepth;
do
{
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
scanf ("%d", &shallowDepth);
if (shallowDepth < 2 || shallowDepth > 5)
{
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}
while (shallowDepth < 2 || shallowDepth > 5);
The problem is if I type characters, such as "asdf", the program goes crazy and repeatedly says "Enter a depth for the shallow end between 2-5 feet: ". I'm not sure why this is exactly happening, but it has to be because it expects an int and I'm passing characters.
So how do I verify that the user inputted data is of int type before trying to store it in a variable? Thanks.
This is happening because with %d scanf will refuse to touch anything that does not look like a number and leaves the text in the buffer. The next time around it will again reach the same text and so on.
I recommend that you ditch scanf for now and try something like fgets and then one of the functions in the strtoXXX family such as strtoul or strtoumax. These functions have a well-defined way of reporting errors and you can easily prompt the user for more text.
For example you could do:
char str[LENGTH];
long x;
if (!fgets(str, sizeof str, stdin)) {
/* Early EOF or error. Either way, bail. */
}
x = strtol(line, NULL, 10);
At this point you could use your number, but be aware that:
You can specify a pointer for strtol to fill and it will point to the first unacceptable character
If the result cannot be represented as a long then strtol will set errno = ERANGE. If you plan to test for this you must set errno = 0 before the strtol
If you want to use scanf you can't test it before. But you don't need too!
In your code if the user doesn't enter a number (or something that starts with a number), scanf returns 0, because it returns the number of parameters it could read.
So you need to check the return value of scanf to check if anything could be read.
Second, you need to remove everything that's still in the puffer.
You can use something like this for that:
while(getchar()!='\n');
If you want to handle files as well, you should catch EOF there, too.
int shallowDepth;
int invalid;
do {
int stat;
invalid = 0;
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
stat = scanf ("%d", &shallowDepth);
if(stat != 1){
invalid = 1;
while(getchar() != '\n');//clear stdin
} else if (shallowDepth < 2 || shallowDepth > 5){
invalid = 1;
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}while (invalid);

Why does this if condition not work even if I enter the correct data?

#include < stdio.h >
#include < process.h >
rec();
main() {
int a, fact;
char question, n, y;
do {
printf("\nEnter any number ");
scanf("%d", & a);
fact = rec(a);
printf("Factorial value = %d\n", fact);
printf("do you want to exit.....(y/n):");
scanf("%s", & question);
}
while (question == n);
exit(0);
}
rec(int x) {
int f;
if (x == 1) return 1;
else f = x * rec(x - 1);
return f;
}
In this program I want to get factorial of the entered number, which I get. But I also want the user to say whether to exit or get the factorial of another number, which I can't do. It asks user but when I enter "n" it exits.
Where is the error?
You want
while (question == 'n');
Or
char question, n = 'n', y = 'y';
Though I find the 2nd version a little redundant.
Either way you need to change
scanf("%s"
to
scanf("%c"
To correctly read in a single char and not a string. Thanks RageD
One problem is the combination of:
char question, n, y;
scanf("%s", &question);
You are using %s to read a null-terminated string into a single character. Even if you hit 'y' and return, you'll be overwriting beyond the end of the variable. This is not good. (The good news is that "%s" skips over white space, including the newline after the number).
You either need to use "%c" in the format:
char question;
scanf(" %c", &question); // NB: The leading space is important!
or you need to use a string format and a string variable (and no &):
char question[10];
scanf("%9s", question);
If you use an array, you need to consider whether to use strcmp(), or whether to compare the first character from the input:
while (strcmp(question, "n") == 0);
while (question[0] == 'n');
You probably got told by the compiler that you'd not declared variable n so you added it. You probably need the loop to end with while (question == 'n');and then get rid of the (now) unused variablen(and the currently unused variabley`).
Note that if you use omit the space in the " %c" format string:
scanf("%c", &question);
then it will normally get the newline after the number, which won't be 'n', so your loop will exit every time, apparently without waiting for you to enter anything. You can finesse that with scanf(" %c", &question); which skips white space before reading a character.
You should test that scanf() received the input you expected each time you use it. The correct test for single item inputs is:
if (scanf(" %c", &question) != 1)
...input failed...
If you need to distinguish between EOF and conversion failure, you can capture the return from scanf():
int rc;
if ((rc = scanf(" %c", &question)) != 1)
...rc == EOF on EOF; rc == 0 on 'conversion failure'...
...a single character input can't easily fail...
...but if someone types 'a' instead of '9' when you're looking for a number...
Getting I/O right using scanf() is distressingly hard. Many experienced programmers simply don't use it; it is too hard to get right. Instead, we use fgets() or POSIX getline() to read a line of data, and then use sscanf() to parse it. There are many advantages to this, but a primary one is that the newline has been eaten so you don't run into problems with the variable question not containing the answer you expect.

Resources