0

I'm trying to write a simple program to read an integer and then a string, then print both to standard output. Ideally, the execution should look something like this:

Input the number.
> 10

Input the string.
> a string

number: 10
string: a string

However, when I run the program, it freezes after the call to scanf() until more input is provided.

Input the number.
> 10
a string

Input the string.
> 
number: 10
string: a string

Why is it waiting for input before fgets() is ever called?

 #include <stdio.h>
 
 int main()
 {
     int number;
     char string[32];
 
     printf("Input the number.\n> ");
     scanf("%d\n", &number);
 
     printf("\nInput the string.\n> ");
     fgets(string, 32, stdin);
 
     printf("\nnumber: %d\nstring: %s\n", number, string);
 }
  • 4
    remove '\n' from scanf – mlwn Jun 13 '22 at 14:40
  • @mlwn Then a newline character is left in the buffer and `fgets` never actually reads anything. – Curtice Gough Jun 13 '22 at 14:41
  • let me check plz – mlwn Jun 13 '22 at 14:43
  • why fgets, and not scanf ?? normally i use fgets to read from a file... – mlwn Jun 13 '22 at 14:45
  • I'm trying to avoid the possibility of a buffer overflow by only reading 32 bytes from stdin – Curtice Gough Jun 13 '22 at 14:46
  • 1
    perhaps useful: https://stackoverflow.com/questions/5918079/fgets-doesnt-work-after-scanf – yano Jun 13 '22 at 14:49
  • Do not mix `scanf` and `fgets` – Eugene Sh. Jun 13 '22 at 14:51
  • 1
    [problems with scanf("%d\n",&i)](https://stackoverflow.com/a/9516244) – 001 Jun 13 '22 at 14:51
  • wow... i see your point... you could do a workaround by removing the second printf, and using only the first one as ```printf("Input the number, followed by a string>\n");``` – mlwn Jun 13 '22 at 14:55
  • @mlwn - removing `\n` in this scenario will not help. `scanf()` is one of the most difficult calls to use correctly, and in some call sequences, it just is not a good option. `fgets()` is far more predictable and easy to control. – ryyker Jun 13 '22 at 14:56
  • @ryyker - yea, I noticed when i tested... – mlwn Jun 13 '22 at 14:57
  • @ryyker As I understand it, `fgets` is primarily used to read `string` data. I'm still pretty new to C so I'm not entirely sure: is there a way to use `fgets` to read an integer? – Curtice Gough Jun 13 '22 at 14:58
  • 1
    No. `fgets()` only reads strings. But if the string is made up of digits it can easily be converted to an `int`. (or `double`, or ...). – ryyker Jun 14 '22 at 12:44

3 Answers3

2

From a previous post...

https://stackoverflow.com/a/5918223/2203541

#include <stdio.h>

int main()
{
  int number;
  int c;
  char string[32];

  printf("Input the number.\n> ");
  scanf("%d", &number);
  do
  {
    c = getchar();
  } while (c != '\n');
  printf("\nInput the string.\n> ");
  fgets(string, 32, stdin);

  printf("\nnumber: %d\nstring: %s\n", number, string);
}
mlwn
  • 1,156
  • 1
  • 10
  • 25
0

"Why is fgets waiting for input before it's even called"

fgets() does not act until it is called, but if when called, and if pointing to stdin and there is content remaining in the stdin stream, it will consume it immediately. If that contents contained EOF, n-1, OR a newline (read link) execution flow will continue.

The problem here is that scanf() (called prior to fgets()) is notorious for doing exactly what it is asked to do. For example, upon user entering 12<return> two recognizable items are entered into stdin, digits and a newline but by using "%d" as the format specifier, only the first of those items is consumed, leaving the \n hanging, until the very next call to fgets(), which accepts it as input, allowing execution flow to resume immediately (as described above), causing the apparent skip you are seeing.

[This is one (of several) examples that will provide a work around for the scanf() issue:

Change this:

 printf("Input the number.\n> ");
 scanf("%d", &number);//leaves the newline

To this:

 char c;
 ...
 printf("Input the number.\n> ");
 scanf("%d%c", &number, &c);//consumes the newline

From comments:

"is there a way to use fgets to read an integer"

Yes, I prefer fgets() coupled with your favorite string to number converter. (There are several) The simplest is this:

char cNum[10];
int num;

printf("Input the number.\n> ");
if(fgets(cNum, sizeof cNum, stdin))
{
     num = atoi(cNum);
}
else //handle error

See also strtol() for a more robust solution.

ryyker
  • 22,849
  • 3
  • 43
  • 87
-1

Alright, so I played around with it some more and did a little more studying on scanf() format syntax, and figured out a solution. Apparently, putting the whitespace character at the end of my scanf call there tells it to keep reading until it finds something AFTER the whitespace, so of course it would hang up there until you give more input.

Remove the whitespace character in the scanf formatter, and add a leading space to the following scanf call.

The reason I had used fgets originally was so I could specify a buffer length to avoid overflow. Apparently, the same effect can be achieved using %32s in the scanf call. The fixed code looks like this:

 #include <stdio.h>
 
 int main()
 {
     int number;
     char string[32];
 
     printf("Input the number.\n> ");
     scanf("%d", &number);
 
     printf("\nInput the string.\n> ");
     scanf(" %32s", &string);
 
     printf("\nnumber: %d\nstring: %s\n", number, string);
 }
  • 1
    Have you tested the code here? You have a parameter mis-match. for the second call to `scanf()`. This will not necessarily show up in a compile, but during run-time. – ryyker Jun 13 '22 at 15:52
  • Yes, it works exactly as intended. – Curtice Gough Jun 13 '22 at 15:54
  • 2
    `&string` should be `string`. i.e. the symbol of an array is equivalent to the address of the first element of that array. i.e. `string` is a `char *` already. – ryyker Jun 13 '22 at 16:00
  • 3
    The leading space is unnecessary in `" %32s"` because `%s` will skip the initial whitespace anyway. It won't do any harm though. If you want to skip the initial whitespace and allow embedded whitespace in the string (e.g. when reading the input `a string`), then you can use `" %32[^\n]"`. `%[` does not skip initial whitespace, so the preceding space character is needed if you want to do that. – Ian Abbott Jun 13 '22 at 16:29
  • 3
    `char string[32];` should be `char string[33];`, or the `%32` should be `%31`. The buffer needs room for the null terminator. – Ian Abbott Jun 13 '22 at 16:34