-4

I was playing with C and wrote this code:

   1 #include<stdio.h>
   2 #define ASK_PROMPT printf("\nDo you want to continue(Y/N):");
   3 main()
   4 {
   5    char main[20], i;
   6    start:
   7    printf("Please enter your string:\n");
   8    gets(main);
   9    printf("\nstring entered was:\n \n%s\n", main);
   10   ASK_PROMPT;
   11   scanf("%c",&i);
   12       
   13   if(i=='Y'||i=='y')
   14       goto start;
   15   getch();
   16   return;
   17 }

when I execute this code, the goto loop is not working correctly. On providing y or Y response to question asked from line 10, loop does work and line 7 is again executed/printed but line 8 was skipped(doesn't wait for the input to be provided).

  • Can anyone explain why it is happening?
  • How do I fix it?
Spikatrix
  • 20,225
  • 7
  • 37
  • 83
  • 1
    This code should the classic example of what **not** to do. – Haris Sep 10 '15 at 13:00
  • 1
    It's almost as you hit more than one key in order to enter `'Y'` or `'y'`. But that would be pretty obvious, so you must have already excluded that possibility in your debugging... – EOF Sep 10 '15 at 13:04
  • means, goto should not be used? I generally don't use goto. but what I want to ask is why its not requesting for input. Does program forget that main is declared already? (please don't mind "main" as that another thing I was trying) – user2507780 Sep 10 '15 at 13:05
  • 3
    see if your problem is not similar to this: http://stackoverflow.com/questions/7898215/how-to-clear-input-buffer-in-c (simplified: input stream needing to be flushed) – fpierrat Sep 10 '15 at 13:06

4 Answers4

2

I would suggest not to use gets.While using gets what if string entered is larger than the size of main[20] .gets won't prevent this and it will cause UB.

Use fgets-

fgets(main,20,stdin);

And reason for your problem is that '\n' after the scanf("%c",&i); which remains in stdin causes this as gets return as it encounter '\n'.

To avoid '\n' you can do this after scanf statement -

int c;
while ( (c = getchar()) != EOF && c != '\n' );
ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • Thank you for replying, I make the changes as per your suggestion. after the changes the first letter of string was deleted/truncated and the problem remain as such. – user2507780 Sep 10 '15 at 13:27
  • @user2507780 You added the code as I said ? The line with `getchar` should be after `scanf("%c",&i);` in your code. – ameyCU Sep 10 '15 at 13:29
  • @CoolGuy Ahh I always forget that it adds null . Thank You !! :) – ameyCU Sep 10 '15 at 13:34
  • even then, its not working, but as per @coolguy suggestion I used scanf("%c%*c", &i); and now its working fine. Thanks for you time and help. Have a nice day. – user2507780 Sep 10 '15 at 13:39
  • @user2507780 Well I don't understand how it didn't work for you as it worked perfectly. Never mind cheers !! – ameyCU Sep 10 '15 at 13:41
  • 1
    I don't why yesterday it was not working, but today it works.. Thanks @ameyCU – user2507780 Sep 11 '15 at 03:58
1

The problem:

After typing yEnter in response to your prompt, the input stream for your program contains the characters 'y' and '\n' (the newline character). The scanf call removes the 'y' character from the input stream, leaving the newline character in place.

gets reads from the input stream until it sees a newline character, which it discards; since the first thing it sees is the newline character from the previous entry, it returns without reading any further input.

The solution:

First, don't use gets. Ever. It was deprecated in the 1999 revision and has been removed completely from the 2011 revision. Using it will introduce a point of failure/security hole in your program. Replace that with a call to fgets instead, just be aware that fgets will try to store the newline in the target buffer if there's room.

Second, you need to consume that trailing newline from the input stream. The answer given by vish4071 shows one approach - use scanf to read the character following your input (which should be the newline) and discard it:

scanf( " %c%*c", &i ); 

The leading blank in the format string tells scanf to skip any leading whitespace, the %c conversion specifier tells scanf to read the next character from the input stream and store its value into i, and the %*c conversion specifier tells scanf to read the next character from the input stream and discard it.

Another option is to consume input until you see a newline:

scanf( " %c", &i );
while ( getchar() != '\n' )  // repeatedly read characters from input stream
  ;                          // until we see a newline

Yet another option is to read your input as another string:

char answer[3]; // space for y/n response plus newline plus 0 terminator
...
if ( fgets( answer, sizeof answer, stdin ) )
{
  if ( tolower( answer[0] ) == 'y' )
  {
    // do stuff
  }
}

You will probably want to check for the presence of the newline character in the answer buffer; if it isn't there, then the user typed in more characters than your buffer was sized to hold, which will likely foul up a future read, and you need to clear that spurious input before your next read operation:

if ( !strchr( answer, '\n' ) )
  while ( getchar() != '\n' )
    ;
John Bode
  • 119,563
  • 19
  • 122
  • 198
0

Fix

Change scanf("%c",&i); to scanf("%c%*c",&i);.

Problem

The \n after inputting i is what is causing gets() not to wait for input.

Good practices

  • Stop using gets(), goto etc. Instead, you can use fgets(), scanf() etc. Never use deprecated functions.
  • Change char array name from main to something else, say m.

Thanks @Coolguy for the comment on use of scanf.

Community
  • 1
  • 1
vish4071
  • 5,135
  • 4
  • 35
  • 65
  • Thanks for replying and advice, but even after the changing the code as per your suggestion it is showing the same problem – user2507780 Sep 10 '15 at 13:22
  • I have tried it on my system and then posted the answer. Which compiler are you using? – vish4071 Sep 10 '15 at 13:23
  • 1
    @user2507780 Use `scanf("%c%*c", &i);`. I would suggest using @ameyCU's solution though – Spikatrix Sep 10 '15 at 13:31
  • I have dev-cpp installed(mingw64 gcc is its part of package) and using its compiler for c (after saving into .c extension) Am I doing this wrong as well? – user2507780 Sep 10 '15 at 13:35
  • I used to use dev-cpp around 4 yrs ago. Used it for sometime but left because it had a very different behavior than standard gcc compiler that linux uses. It does not give exact warnings or exit status (segfault etc) if code crashes, so its really hard to debug. – vish4071 Sep 10 '15 at 13:37
  • @CoolGuy, thanks for the correct formatting of scanf. Can you explain the difference in behavior of scanf when we use `"%c%\n"` or when we use `"%c%*c"` – vish4071 Sep 10 '15 at 13:39
  • Thank you @vish4071,as per the suggestion of "coolguy" from comments, code is now good. thank you for your time and help. have a nice day – user2507780 Sep 10 '15 at 13:42
  • @vish4071 Sure. Here `"%c%\n"`, `\n` is a whitespace character. And when there is a whitespace character in the format string of `scanf`, it will scan and discard whitespace characters, if any, **until the first non-whitespace character**. On the other hand, `%*c` simply scans and discards a character. – Spikatrix Sep 10 '15 at 13:46
  • thanks @CoolGuy. When I did not know how to scan multiple words with scanf, I used gets() even when I did not want to and have faced similar problems. I found this solution to scan newline myself and it did rectify for many usecases and somehow worked. If I knew or could think of scanning a char * instead, it'd have been great. Thanks anyway. – vish4071 Sep 10 '15 at 13:51
  • Not required. Its better formatting now. Thanks for the edit as well :) – vish4071 Sep 10 '15 at 13:52
  • 1
    `scanf("%c%*c",&i);` scans _any_ 2 characters. `scanf("%c%*1[\n]",&i);`, as ugly as it is, scans one character and then a potential following `'\n'`. IAC better to go with [`fgets()`](http://stackoverflow.com/a/32502901/2410359) – chux - Reinstate Monica Sep 10 '15 at 15:02
0

Minimal fix to existing code, just use gets(main) a second time. If the code is going to use ASK_PROMPT;, then the define for ASK_PROMPT doesn't need a trailing ; . As already suggested, you can use fgets() instead of gets(), and use a proper loop instead of start: and goto.

#include<stdio.h>
#define ASK_PROMPT printf("\nDo you want to continue(Y/N):")
int main()
{
    char main[20];
    start:
    printf("Please enter your string:\n");
    gets(main);
    printf("\nstring entered was:\n \n%s\n", main);
    ASK_PROMPT;
    gets(main);
    if(main[0]=='Y'||main[0]=='y')
        goto start;
    getch();
    return(0);
}
rcgldr
  • 27,407
  • 3
  • 36
  • 61