Why is my call to strcmp not working as expected? - c

I'm new to C and programming in general. It's a guessing game where the user has a certain amount of guesses to guess whatever the hidden message is.
Unfortunately, even if I enter the correct word, it does not register it as being correct.
#include <stdio.h>
#include <stdlib.h>
Lost(){
printf("You lost");
}
Won(){
printf("You won");
}
int main(void)
{
char HiddenMessage[7] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while(Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0){
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries){
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0){
Won();
}
}
return 0;
}
How do I structure this to ensure that if a Guess occurs correctly within the number of tries it runs Won();

You should have received a warning about:
char HiddenMessage[7] = "Cameron";
if your compiler was doing its job properly :-)
Seven characters is not enough to store Cameron and the end-of-string marker. Try it again with:
char HiddenMessage[] = "Cameron";
This will make sure enough characters are set aside for the full string.
If you're interested, this is covered in C11 6.7.9 Initialization /14 (emphasis added):
An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
So what your code will give you is the sequence of characters { 'C', 'a', 'm', 'e', 'r', 'o', 'n' } but with no \0 at the end to make it a C string. That means strcmp cannot be guaranteed to give you the results you want (it will possibly run off the end of the array during the comparison).
And, as an aside, scanf with the unbounded %s format specifier is considered rather dangerous in non-trivial code, since there's no way to protect against buffer overflow.
If you're looking for a fairly bullet-proof user input function that can detect and mitigate this, have a look at this earlier answer of mine.
It allows prompting, prevents buffer overflow, properly handles line-based user input (no half lines delivered to user) and so on.
It's what I use for simple console applications (where no better user input method is available).

There are, essentially, four errors in your code:
As paxdiablo has stated, your HiddenMessage array is not large enough to hold the text you have given plus the nul terminator.
You need to define (or at least declare) your Lost and Won functions before you use them.
The definitions of these two functions are incorrect, as you have not specified a return type: void is suitable for functions that don't return anything.
You must include stdio.h to get the definitions of printf and scanf
Here's a fixed (and working) version of your code with the corrections made:
#include <stdlib.h>
#include <stdio.h> // MUST include this for "printf" and "scanf" functions
// You can keep these DEFINITIONS after "main" if you like, but then you'll need to
// have "pre-declarations" of them (the two commented lines immediately below).
// void Lost();
// void Won();
void Lost() {
printf("You lost");
}
void Won() {
printf("You won");
}
int main()
{
char HiddenMessage[8] = "Cameron"; // Added enough space for the nul terminator
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0) {
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries) {
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0) {
Won();
}
}
return 0;
}
Feel free to ask for further clarification and/or explanation.

Another significant problem is your failure to validate any of the input. Any time (means ANY time) you take input, you must validate the input succeeds before relying on the data. Failure to validate invites undefined behavior.
Validations are simple, if you read input with a function check the return to validate you got all the input you were expecting. For example:
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
(note: the use of the field-width modifier to limit the number of characters that can be read. Failing to control the number of characters than can be input will read to attempt to write beyond the bounds of your array if the user enters 50 (or more) characters.)
Your logic is a bit awkward. All you need to do is loop continually until the user either wins the game or exhausts the number of tries available. Rearranging your logic slightly, you could do something like:
(Edited to empty-stdin)
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
Putting it altogether in an example, you could do:
#include <stdio.h>
#include <string.h>
#define MAXTRIES 5
#define MAXGUESS 50
int main (void) {
char hiddenmsg[] = "Cameron",
guess[MAXGUESS] = "";
int tries = 0;
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
}
Example Use/Output
Typical try and fail game:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Alligator
try no. 2 - Guess the seceret word: Campers
try no. 3 - Guess the seceret word: Scam
try no. 4 - Guess the seceret word: Horses
try no. 5 - Guess the seceret word: Bananna
you have exhausted all guesses - you lost :(
A lucky win:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Lightning
try no. 2 - Guess the seceret word: Thunder
try no. 3 - Guess the seceret word: Geroge
try no. 4 - Guess the seceret word: Cameron
congratulations - you won.
(Testcase After Edit w/Example from Comment)
$./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCameron
try no. 2 - Guess the seceret word: foo
try no. 3 - Guess the seceret word: Cameron
congratulations - you won.
Look things over and let me know if you have any further questions.

As strcmp compares the difference between each character till the end of string that is \0 So your buffer needs to be large enough to hold your string i.e. char HiddenMessage[8] = "Cameron";
PS you can avoid a lot of lengthy code
#include <stdlib.h>
int main()
{
char HiddenMessage[8] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries)
{
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (strcmp(HiddenMessage, Guess) == 0)
{
Won();
return 0;
}
}
Lost();
return 0;
}
void Lost()
{
printf("You lost");
}
void Won()
{
printf("You won");
}

There are multiple problems with your code. The size of HiddenMessage reported by #paxdiablo is just one of them.
You should use fgets instead of scanf because scanf won't consume the newline. You will be stuck on the second iteration.
You increment Tries and test it against MaxTries before testing if the guess was correct. As a consequence the program will tell that the user lost before testing the validity of the last guess.
Once the user guessed the word and won, you must break from the while loop and terminate the program. With your code, after the program reported that the user won, it will ask for another guess if it wasn't the last guess.

Related

Why is the following code not allowing me to get user input with fgets yet works with scanf?

This is a short excerpt from a bigger program, but the rest of the program is irrelevant since I think I was able to isolate the issue. I suspect that it has something to do with the way I'm using fgets. I've read that it's preferable to use fgets over scanf, but I can't seem to get it to work properly here. When I use the following code, the program doesn't give me a chance to enter the number (but simply skips to the while loop which checks if the number entered is in the correct range):
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
fgets(input, 1, stdin);
// scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
Here's the output I get:
~/workspace/extra_stuff/hash_tables/ $ ./test2
how many words would you like to enter? (1-10): the number of words is: 0
please enter a number between 1 and 10:
As you can see, it never let me enter the number, but simply moved on to the next step seemingly assuming I didn't enter anything.
If I change the code as follows, everything works as planned:
#include <stdlib.h>
#define SIZE 10
int main(void)
{
// ask user for how many items to store
printf("how many words would you like to enter? (1-%i): ", SIZE);
// save number of words user would like to store
char *input = malloc(sizeof(char));
// fgets(input, 1, stdin);
scanf("%c", input);
int words = atoi(input);
printf("the number of words is: %i\n", words);
while (words < 1 || words > SIZE)
{
printf("please enter a number between 1 and %i: ", SIZE);
scanf("%i", &words);
}
}
P.S.: I do realize that if using scanf, I could immediately store the input to an int variable w/o using atoi to convert char to int; however it seems that fgets requires a char *, so that's why I chose this route. Also, I realize that I'm supposed to free(input) later.
Can someone explain this behavior? Thanks.
EDIT:
Thanks to everyone who has replied so far! Some helpful suggestions there, but it looks like I'm having the same issue further in my program. Here's the code excerpt:
// ask for strings
for (int j = 0; j < words; j++)
{
char buffer[4096];
// fgets(buffer, 40, stdin);
// name=calloc(NAME_SIZE, sizeof(char));
// fgets(name, NAME_SIZE, stdin);
// printf("size of (array[j]->next)->text is: %lu\n", sizeof((array[j]->next)->text));
printf("please enter string #%i: ", j);
fgets(buffer, 4096, stdin);
printf("you've entered: %s", buffer);
int length = strlen(buffer);
printf("word length: %i\n", length);
}
When I run the program, it once again doesn't give me a chance to enter my input when it's supposed to:
please enter string #0: you've entered:
word length: 1
EDIT #2:
After working through David's answer and referencing other people's comments and other SO threads, I've come up with the following version of the code, which first asks the user for the number of words they'd like to enter (and validates the input) and then asks the user to enter those words (again, validating the input). It seems to be compiling w/o errors and warnings and functioning properly, though I am not 100% sure I've tested all the possible things that could go wrong with the user input, and there are some bits of the code I still don't completely understand (I'll list them below) -- if anyone has time/desire/patience to look through it and tell me if I can still improve something, please let me know. My goal is to use this code in another program that will ask for user input and store the entries in a hash table.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_SIZE_WORDS 4096
#define BUF_SIZE_NUMBERS 256
#define MAX_WORDS 10
int word_input(int num_words);
void empty_stdin();
int main(void)
{
int num_words = 0, /* number of words to enter */
word_count_check = 0; /* word count */
char buffer[BUF_SIZE_NUMBERS] = ""; /* buffer of sufficient size for input */
for (;;) /* loop continually until valid input of NUMBER OF WORDS USER WANTS TO ENTER or user cancels */
{
printf ("how many words would you like to enter? [1-%d]: ", MAX_WORDS);
// check for cancellation of input
if (!fgets (buffer, BUF_SIZE_NUMBERS, stdin))
{
fputs ("user canceled input\n", stderr);
return 1;
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a value\n");
continue;
}
size_t inlength = strlen(buffer);
// validate length < BUF_SIZE_NUMBERS - 1
if (inlength >= BUF_SIZE_NUMBERS - 1)
{
fputs ("input exceeds allocated buffer size\n", stderr);
return 2;
}
if (inlength && buffer[inlength - 1] == '\n')
{
// printf("hurray!\n");
buffer[--inlength] = 0;
}
else if (inlength == BUF_SIZE_NUMBERS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_NUMBERS);
empty_stdin();
continue;
}
// make sure user actually entered a proper int
if (sscanf (buffer, "%d", &num_words) != 1) /* sscanf is used for conversion */
{
fputs ("invalid conversion to int; please provide valid input\n", stderr);
continue;
}
// check if the number entered is out of range
if (num_words < 1 || num_words > MAX_WORDS)
fprintf (stderr, "%2d out of valid range.\n", num_words);
else
break; /*if the input has been validated, we can now break out of the for loop */
}
// call the word_input function and store its return value in word_count_check
word_count_check = word_input(num_words);
// check if the number of words processed equals to the number requested by the user
if(word_count_check == num_words)
{
printf("success!\n");
}
else
{
printf("something went wrong, since word_count_check != num_words...\n");
}
}
int word_input(int num_words)
{
int word_count = 0;
for(;;) /* loop until word_count == num_words is achieved */
{
// declare an array for storing input string
char buffer[BUF_SIZE_WORDS];
char valid_input[BUF_SIZE_WORDS];
// prompt user for input
printf("please enter a string: ");
// get input and check for CTRL+D
if (!fgets(buffer, BUF_SIZE_WORDS, stdin))
{
fputs ("user canceled input\n", stderr);
exit(1);
}
// check if user simply hit enter w/o typing anything
if(buffer[0] == '\n')
{
printf("please enter a word that's more than 0 characters\n");
// empty_stdin();
continue;
}
size_t inlength = strlen(buffer);
// check if user input exceed buffer size
if (inlength >= BUF_SIZE_WORDS - 1)
{
empty_stdin();
fputs ("input exceeds allocated buffer size, please try again\n", stderr);
continue;
}
// check if the user entered too many characters
if (inlength == BUF_SIZE_WORDS - 1) /* the line was too long */
{
printf("you've entered too many characters... please stick to a maximum of %i\n", BUF_SIZE_WORDS);
empty_stdin();
continue;
}
if (inlength && buffer[inlength - 1] == '\n')
{
buffer[--inlength] = 0;
// get rid of trailing spaces using sscanf
sscanf(buffer, "%s", valid_input);
// figure out the length of the word the user entered
int word_length = ((int) strlen(valid_input));
printf("string length: %i\n", word_length);
// print out the word entered by the user one character at a time
printf("you've entered: ");
for (int i = 0; i < word_length; i++)
{
printf("%c", valid_input[i]);
}
printf("\n");
// increment word count
word_count++;
printf("word_count = %i\n", word_count);
if (word_count == num_words)
{
return word_count;
}
}
}
}
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
things I don't completely understand yet:
1)
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
--- does this simply check if the user manually entered EOF (with ctrl+d), or does it check for something else too?
2) calling the empty_stdin() function below seemed to be causing some kind of a weird hang-up where it looked like the program was expecting further input from me as opposed to just going on to the next step, especially when I used it frequently (I figured why not clear the input stream every time the user types in something weird?) and/or when I decreased the buffer to something very small and then purposefully entered too many characters..
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
3) eventually I want to use some of this code to load a dictionary from a text file (instead of user input) and store it in a hash table, and, in another version, in a trie. Besides using isalpha() to make sure we're only storing words that have letters in them, are there any other checks/validations that need to happen when processing the input, beside the ones above? Should any of the above checks be skipped?
There is no magic involved in dealing with strings in C -- but you do need to put your accounting hat on... Why? When dealing with input, you have to not only account for the number of characters that you place into your buffer (or wherever you are storing your input), but you also must account for the characters that remain in your input stream!
This is particularly true when using any of the scanf family of function for input. Why? Because on a matching or input failure, processing (reading and removing) characters from your input buffer (stdin here) stops, no further characters are read, and any character causing the matching failure remains unread in your input stream, just waiting to bite you again on your next attempted read.
Compounding this bewilderment for new C programmers is the fact that some conversion specifiers consume leading whitespace (e.g. space, tab, newline,...) and others do not. Your numeric conversion specifiers (along with "%s") consume leading whitespace while "%c" and "%[...]" do not.
All of which are the primary reasons new C programmers are encouraged to use line-oriented input functions like fgets or POSIX getline to handle user input (because they read the entire line at a time -- including the trialing '\n') freeing the new programmer for having to account for ending whitespace or offending characters not converted in the event of a matching failure...
Using fgets followed by a sscanf provides the additional benefit of allowing separate validation of (1) the read of input; and (2) the parse and conversion of input into the needed values.
(note: the only caveat with line-oriented input functions is that they read and include the trailing '\n' in the buffer they fill -- so you will need to "trim" the trailing whitespace as required. You don't want stray '\n' characters dangling off the end of the strings you are storing.)
That said, there will be times when reading input with the scanf family of functions makes sense. There is nothing wrong with doing so, so long as you validate the return every time and handle all three possible conditions:
the user presses ctrl+d on Linux to generate a manual EOF (ctrl+z on windoze);
you handle the matching or input failure cases, including removing any offending characters from your input buffer before your next attempted read; and finally
you have good input (the return indicates all the conversions anticipated, took place).
There is no magic to any of it, but it does take understanding the possible error conditions and handling each of them on every input.
In your case, let's look at your task of getting the number of words to enter from the user. Here you were attempting to read with fgets (that's good!), but you failed to provide sufficient storage to hold the input. When reading small amount of text from the user, a simple array with automatic storage type is all you need. However, you need to size the buffer accordingly (and do NOT skimp on buffer size).
There is no golden rule, but if I had users entering text to convert to a single number, then I would feel good with a 256 character buffer (which provides more than enough to hold the input of any valid number, plus another 230 some-odd characters to handle the time the cat steps on the keyboard, etc..)
For example, taking input from the user and getting the number of words to enter could be done in a manner similar to the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords) /* validate nwords in range */
fprintf (stderr, " %2d out of valid range.\n", nwords);
else /* good input received, break loop */
break;
}
(note: your while loop has been converted to a loop that will loop continually until valid input between 1 < value < SIZE is entered. The condition simply causes control to break; the loop at the point good input is received)
This loop presents a classic fgets/sscanf read and parse of information from the line of input entered by the user. You can parse the number from the line any way you like (but don't use atoi() -- it provides absolutely zero error checking of the conversion). You can use strtol (with proper validation) and you can simply use a pointer to walk-down-the-buffer, picking out digits, converting them from their ASCII to numeric value and then multiplying by 10 and adding as your go. Any way is fine so long as you validate, validate, validate each part of the operation.
Now turning to reading each of the words the user is supposed to enter, we will ignore conventional wisdom and use scanf for the task, but we will handle all three possible cases of the return every time. We will also add a counter to keep track of the valid inputs provided by the user and only exit the loop when we have that number of valid integers provided (or the user cancels by generating a manual EOF).
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
Note: the emptying of any offending characters from stdin can be turned into a convenient function so that you do not have to duplicate the loops each time you need to clear stdin during your input routine. You can replace it with a simple function, e.g.
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
That will help keep your code tidy. I'll let you incorporate that above.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define SIZE 10 /* good form defining a constant! */
#define MAXC 256 /* max characters for buffer */
int main (void) {
int nwords = 0, /* number of words to enter */
words = 0, /* each word */
wc = 0; /* word count */
char buf[MAXC] = ""; /* buffer of sufficient size for input */
for (;;) { /* loop continually until valid input or user cancels */
printf ("number of words to enter? [1-%d]: ", SIZE);
if (!fgets (buf, MAXC, stdin)) { /* validate ALL user input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* validate length < MAXC - 1 and buf[length-1] == '\n' here */
if (sscanf (buf, "%d", &nwords) != 1) { /* sscanf for conversion */
fputs (" error: invalid conversion to int.\n", stderr);
continue;
}
if (nwords < 1 || SIZE < nwords)
fprintf (stderr, " %2d out of valid range.\n", nwords);
else
break;
}
printf ("\nnumber of words entered: %d\n", nwords);
for (; wc < nwords;) { /* loop continually */
int rtn = 0; /* scanf return */
printf ("please enter a number between 1 and %d: ", SIZE);
rtn = scanf ("%d", &words); /* valdate ALL user input */
if (rtn == EOF) { /* handle EOF (manual) */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn == 0) { /* handle "matching failure" */
int c = getchar(); /* remove offending chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
fputs (" error: invalid integer input\n", stderr);
continue;
}
else { /* valid integer received */
int c = getchar(); /* remove any extra chars from stdin */
while (c != '\n' && c != EOF)
c = getchar();
if (words < 1 || SIZE < words) /* validate in-range */
fprintf (stderr, " %2d - invalid! (1 < valid < %d)\n",
words, SIZE);
else /* good input, increment word count */
printf (" word[%2d]: %3d\n", ++wc, words);
}
}
}
Example Use/Output
$ ./bin/getintstdin
number of words to enter? [1-10]: five, maybe six?
error: invalid conversion to int.
number of words to enter? [1-10]: -2
-2 out of valid range.
number of words to enter? [1-10]: 3
number of words entered: 3
please enter a number between 1 and 10: two? three?
error: invalid integer input
please enter a number between 1 and 10: 2
word[ 1]: 2
please enter a number between 1 and 10: -2
-2 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 11
11 - invalid! (1 < valid < 10)
please enter a number between 1 and 10: 3
word[ 2]: 3
please enter a number between 1 and 10: 4
word[ 3]: 4
Note all the invalid inputs above and how the code handles each. So long as the input does not exceed 255-characters with fgets, the code will gracefully respond to inputs that are not valid integers (regardless how many are given) and it will respond to integer inputs that are out-of-range.
The code isn't much longer than the code you posted, but it addresses the possible error conditions that could arise and then handles the errors. When you boil it all down, that is what coding is all about. Look things over and let me know if you have further questions.

Reversing String in c using loops....

I have created a code reverse a string but for some reason it is not working. But I think my logic is right. Then why is it not working??
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
printf("\nThe Reverse is: ");
while (i<=0) {
printf("%s",words[i]);
i--;
}
return 0;
}
While you already have an answer, there are a few additional points you need to consider before you have a solution that doesn't have the potential to invoke Undefined behavior.
First, always, always validate all user input. For all you know a cat could have gone to sleep on the 'L' key (with millions being entered), or a more likely case, the user just decides to type a 100-char sentence (or more) which leaves 'words' as an array of chars that is NOT nul-terminated and thus not a valid string in C. Your loop to get the length now invokes Undefined Behavior by reading beyond the end of words off into the stack until the first random '0' is encounter or a SegFault occurs.
To prevent this behavior (you should really just use fgets) but with scanf you can provide a field-width modifier to prevent reading more than length - 1 chars. This insures space for the nul-terminating character.
Further, the "%s" conversion-specifier stops conversion on the first whitespace character encountered -- making your "Enter a .../sentence" an impossibility because scanf ("%s", words) will stop reading after the first word (at the first whitespace.
To correct this problem (you should really just use fgets) or with scanf you can use a character class (stuff between [...]) as the conversion specifier that will read until a '\n' is encountered., e.g. scanf ("%[^\n]", words). However, recall, that is still not good enough because more than 99-chars can be entered leaving the string un-terminated at 100 and invoking Undefined Behavior at character 101 (off the end of the array).
To prevent this problem (ditto on fgets), or include the field-width modifier, e.g. scanf ("%99[^\n]", words). Now no more than 99-chars will be read regardless of the cat sleeping on the 'L' key.
Putting that altogether, you could do something like:
#include <stdio.h>
#define MAXC 100 /* if you need a constant, define one */
int main(void) {
char words[MAXC] = "";
int i = 0, rtn = 0; /* rtn - capture the return of scanf */
printf ("Enter a word/sentence : ");
if ((rtn = scanf ("%99[^\n]", words)) != 1) { /* validate ! */
if (rtn == EOF) /* user cancel? [ctrl+d] or [ctrl+z]? */
fprintf (stderr, "user input canceled.\n");
else /* did an input failure occur ? */
fprintf (stderr, "error: invalid input - input failure.\n");
return 1; /* either way, bail */
}
for (; words[i]; i++) {} /* get the length */
printf ("Reversed word/sentence: ");
while (i--)
putchar (words[i]); /* no need for printf to output 1-char */
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/strrevloops
Enter a word/sentence : My dog has fleas.
Reversed word/sentence: .saelf sah god yM
Look things over and let me know if you have any further questions.
There are few mistakes in your program.
After you have reached the end of the string.You should do i-- as your array index of i will be pointing to '\0'.
Your while loop checks for <= but it should be >=.
Use %c for printing chararcters. %s is used to print strings and not char.
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
i--;
printf("\nThe Reverse is: ");
while (i>=0) {
printf("%c",words[i]);
i--;
}
return 0;
}

want to call fgets() multiple times based on users need

User enters '1' or '0' choice to continue getting string using fgets(). So when the user enters the choice, fgets reads it from console. I am storing it in another variable. But fgets gets the choice and stores it in messages. I have tried using fflush(stdin) after receiving the choice. Please help me out.
int main() {
int choice=1;
char *message;
int i=0;
while (choice == 1) {
fflush(stdout);
printf("Enter the message: ");
fflush(stdout);
message = fgets(message,200,stdin);
while (message[i]!='\n') {
i++;
}
message[i] = '\0';
send_message(message);
printf("\nType '1' to continue or '0' to quit: ");
scanf("%d",&choice);
fflush(stdin);
}
}
It looks like you're trying to scanf() to read the user's input -- this is inherently dangerous. (See https://www.reddit.com/r/learnprogramming/comments/1d0w4x/c_scanf_d_but_error_if_user_enters_a_character/).
I'd recommend either using %s for your format string, or better yet, build a subroutine to do safe input and parse it the old-fashioned way, such as something along these lines:
/* getsafe() - Generic input using the preferred input method rather than gets() */
#include <stdio.h>
#include <string.h>
char *getsafe(char *inpstr,int inpsiz) {
char *seachr; /* Result of search via strchr() */
if (inpstr==NULL) {
return(NULL);
}
if (fgets(inpstr,inpsiz,stdin)==NULL) {
return(NULL);
}
seachr=strchr(inpstr,'\n');
if (seachr!=NULL) *seachr=0;
return(inpstr);
}
That way you can specify the buffer length and provide a string (array of characters) of sufficient length as to prevent buffer overruns (security issue), and then parse the [0] position in that array for your answer.
#define ANSSIZ 80 /* Maximum allowed size of user answer */
char usrans[ANSSIZ]; /* User Answer */
printf("Enter 'y' or 'n': ");
getsafe(usrans, ANSSIZ-1);
There's a lot of problems with this - It probably belongs on Code Review
However, here is a critique on some of the major problems
int main() {
int choice=1;
char *message; // This is a pointer, but is not malloc'ed. You might want "char message[200]" instead?
int i=0; // This is the only time "i" is set to 0. It needs to be reset at the start of the loop
while (choice == 1) {
fflush(stdout); // No need for this
printf("Enter the message: ");
fflush(stdout);
message = fgets(message,200,stdin);
while (message[i]!='\n') { // Why not use strlen?
i++; // "i" can keep growing forever if there is no newline (if someone entered 199 characters before pressing enter)
}
message[i] = '\0'; // fgets does this for you - The past loop was pointless
send_message(message);
printf("\nType 'y' to continue or 'n' to quit: "); // You forgot to flush here!
scanf("%d",&choice); // I don't think this will result in a 0 or 1 output... %d is for a digit, and you're asking the user for y or n.
fflush(stdin); // This is invalid and unneeded - You can't flush stdin
}
}

Hangman code in C

I am trying to write a hangman program in C.
Occasionally, some sentences appear twice in the program's output. The program is also intolerant of user inputs other than 1. How can I fix this?
Here's my code:
#include <stdio.h>
#include <string.h>
/* contant declarations */
#define NUM_TRIES_ALLOWED 10
int main() {
/* variable declarations */
int num_letters = 0, /* length of word char array */
count = 0, /* for word char array */
tries = 0, /* total tries user has used */
num_vis_chars = 0, /* # of visible characters */
correct_guesses = 0, /* # of correct guesses */
correct_flag = 0, /* was guess correct? */
repeat_flag = 0, /* was guess a repeat? */
choice;
char guess, guessword;
/* array declarations */
char word[255] = " ";
char incorrect_letters[255] = " ";
/* get word */
puts("Enter a word for player to guess.");
gets(word);
("Ready to start!\n");
num_letters = strlen(word);
char visible_word[num_letters]; /* displays correct guesses */
/* initialize visble_word */
for (count = 0; count < num_letters; count++)
visible_word[count] = '*';
visible_word[num_letters] = '\0';
if (guess == visible_word[count]) {
while (tries < NUM_TRIES_ALLOWED) {
printf("The word is: %s\n\n", visible_word);
printf("Number of turns remaining: %d", NUM_TRIES_ALLOWED - tries);
printf(
"\nWould you like to guess the word [w] or guess a letter [l]:");
choice = getchar();
if (choice == 'l') {
printf("\nWhat letter have you chosen?:\t ");
scanf(" %c", &guess);
}
/* match guess against previous guesses */
for (count = 0; count < num_letters; count++)
if (guess == visible_word[count]
|| guess == incorrect_letters[count]) {
repeat_flag = 1;
correct_flag = 1;
break;
}
if (repeat_flag == 0)
/* check for matches in string */
for (count = 0; count < num_letters; count++) {
if (guess == word[count]) {
visible_word[count] = guess;
correct_guesses++;
printf(
"\n**************************************************************\n\n");
printf("Good choice!\n\n");
if (correct_guesses == num_letters) {
puts("\n\nCONGRATULATIONS! You guessed the word!");
printf("WORD: %s\n\n", visible_word);
exit(0);
}
correct_flag = 1;
}
}
if (correct_flag == 0) {
incorrect_letters[tries] = guess;
tries++;
printf(
"\n**************************************************************\n\n");
printf("Bad choice!\n\n");
}
/* reset flags */
repeat_flag = 0;
correct_flag = 0;
}
puts("You did not guess the word.");
printf("WORD: %s\n\n", visible_word);
}
if (choice = 'w') {
printf("\nWhat word have you chosen?:\t ");
scanf("%s", &guessword);
if (guessword == word) {
printf("CONGRATULATIONS! You guessed the word!");
} else {
printf("nops");
}
}
return 0;
}
I would suggest providing functions for this code. The code is difficult to read and follow. Start with first creating a menu function, then get user input function; also do checks with-in the function that verify that the input if valid before it sends a character back. Then check to see if the guess is correct and post the response using a function. You will find that if you draw out your code and create the necessary functions it will be much easier to follow and debug.
print_menu() displays the options for the user and an exit choice.
get_user_input() gets the user input with checking to see that it is valid input
print_remaining() prints remaining and guessed characters of the word at play
Note if you for instance are getting a char from the user and they enter a letter.
scanf_s("%c", &guess, _countof(guess));
Note: _countof(ArrayName) simply just verifies that there is enough space for the placement of the char or string that is going to be assigned. Make sure to #include "stdlib.h" file for this function.
The reason for using scanf_s is that it reads over whitespaces. It dose not read the bad input and if you encounter some from the user, it puts the bad input back into the input buffer where it got if from. The scanf_s() has a return value and its value is going to be how many correct input responses were filled. Meaning this if %c actually read a char then the return value will be 1. if you got a 4 for the entry it will put the four back and return a 0. Good way to read input. getchar() also works well with this but has some detailed conditions that could cause some grief if not understood. Would be something to consider.
while(scanf_s("%c", &guess, _countof(guess)) != 1 )
// clear the input buffer and then try again.
Then you just clear the input buffer with
while(getchar() != '\n')
continue;
Above: Clears everything in the input buffer up to and including the newline char. Then, ask the user to enter valid input this time.
If you getting stuff that is printing twice that tells me that the loop is running past its exit condition. If your exit condition has anything to do with reading input. The above loop will take care of any information chillen in the input buffer. Hope this helps. Focus on only calling functions in the actual code. Makes debugging much easier.

How to check if the user input an integer using scanf

I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.

Resources