0

I'm the learning the very basics of C programming right now, and I'm practicing the scanf() function. This very, very simple program takes in a number and a letter and uses the printf() function to display the number and letter.

If I ask the user to enter the number first, the program works, i.e., asks for a number, asks for a letter, and prints the input. If I ask for the letter first, the program asks for a letter but then doesn't ask for a number.

I've tried multiple ways and reordered it, but it doesn't seem to work.

This works:

#include<stdio.h>
 void main(){
    int number;
    char letter;

    printf("Enter letter...");
    scanf("%c", &letter);

    printf("Enter number....");
    scanf("%d", &number);

    printf("Number entered: %d and letter entered: %c.\n", number, letter);
 }

But, this combination doesn't work:

#include<stdio.h>
void main(){
    int number;
    char letter;

    printf("Enter number....");
    scanf("%d", &number);

    printf("Enter letter...");
    scanf("%c", &letter);

    printf("Number entered: %d and letter entered: %c.\n", number, letter);
 }

The output I get for the first program is:

Enter letter...a
Enter number....9
Number entered: 9 and letter entered: a.

Which is correct

But the second case doesn't work, and I don't get why it wouldn't work -- skips the "enter letter" part

the output is

Enter number....9
Enter letter...Number entered: 9 and letter entered: 
.

Context: I entered "a" for letter and "9" for number in the above example.

Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
Jacques
  • 37
  • 4
  • `scanf` sucks. You've just discovered #12 of its 17 stupid problems that are almost perfectly designed to trip up newcomers. – Steve Summit Jul 18 '19 at 14:30
  • 1
    @acques Use scanf(" %c", &letter); See the blank before % – Vlad from Moscow Jul 18 '19 at 14:30
  • @SteveSummit It has limitations, that doesn't mean that it sucks. You just have to know what it can and can't do. – Thomas Jager Jul 18 '19 at 14:31
  • @ThomasJager No, I'm sorry. I stand by my earlier statement. `scanf` is useless, and the language (and especially C learners) would be better off without it. Learning to know what it can and can't do is simply more trouble than it's worth. – Steve Summit Jul 18 '19 at 14:32

4 Answers4

0

Specifying scanf the following way

scanf("%c", &letter);

does not skip white spaces and can read for example a new line character stored in the input buffer when the user pressed Enter entering previous data.

Use instead

scanf(" %c", &letter);
       ^^^

to skip white spaces.

From the C Standard (7.21.6.2 The fscanf function)

8 Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier.

and

5 A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read.

Pay attention to that according to the C Standard the function main without parameters shall be declared like

int main(void)

From the C Standard (5.1.2.2.1 Program startup)

1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

int main(void) { /* ... */ }
Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
  • Thank you for your quick reply... I was wondering if I use ```int main(void)``` then do I need to ```return 0``` at the end, stylistically wise or no? Is ```void main()``` not conventional? – Jacques Jul 18 '19 at 14:37
  • @Jacques No, you need not. It will be returned "by default". And stylistically there is nothing bad especially for such small programs. – Vlad from Moscow Jul 18 '19 at 14:40
  • This is a duplicate question. Don't answer it; instead, flag it as a duplicate. – S.S. Anne Jul 18 '19 at 14:48
  • @JL2210 I looked for a duplicate before posting my answer, but couldn't find a good one. Is it you that's downvoting all the answers here? – Steve Summit Jul 18 '19 at 14:53
  • @SteveSummit There are *many, many, many* questions covering this topic. – S.S. Anne Jul 18 '19 at 14:55
0

Add a space before %c. So, change this:

scanf("%c", &letter);

to this:

scanf(" %c", &letter);

As I have written in caution when using scanf, this will make scanf eat the whitespaces and special characters (otherwise it will consider them as inputs).

Here, it will consume the newline character, on other words, the Enter you press, after typing your input!

To be exact, in your example, think of what the user (in this case you) do:

  • You type 9
  • You press Enter
  • You type 'a'
  • You press Enter

Now, when you input something, from your keyboard in this case, this will go into the Standard Input buffer, where it will patiently await to be read.

Here, scanf("%d", &number); will come and read a number. It finds 9 in the first cell of the STDIN buffer, it reads it, thus deleting it from the buffer.

Now, scanf("%c", &letter); comes, and it reads a character. It finds the newline character, that's the first Enter you pressed, and the function is now happy - it was told to read a character, and that's exactly what it did. Now that newline character gets deleted from the buffer (now what's left in there is 'a' and a newline character - these two are not going to be read, since there is no other function call. left for that).

So what changes if I write scanf(" %c", &letter); instead?

The first scanf will still read the number 9, and the buffer will now have a newline character, the 'a' character, and another newline character.

Now scanf(" %c", &letter);` is called, and it goes to search for a character to read in the STDIN buffer, only that now it will first consume any special characters found.

So there it goes to the buffer, it firstly encounters the newline character, it consumes it.

Then, it will encounter 'a', which is not a special character, and therefore it will read normally, and stored to the passed variable in scanf.

The last newline character will remain in the STDIN buffer, untouched and unseen, until the program terminates and the buffer gets deallocated.


Tip: You probably meant to write int main(void), instead of void main(). Read more in What should main() return in C and C++?

gsamaras
  • 66,800
  • 33
  • 152
  • 256
0

It turns out there's a surprising difference between %d and %c. Besides the fact that %d scans potentially multiple digits while %c scans exactly one character, the surprising difference is that %d skips any leading whitespace, while %c does not.

And then there's another easily-overlooked issue when you're using scanf to read user inputs, which is, what happens to all those newlines -- the \n characters -- that get inserted when the user hits the ENTER key to input something?

So here's what happened. Your first program had

printf("Enter letter...");
scanf("%c", &letter);

printf("Enter number....");
scanf("%d", &number);

The user typed a letter, and ENTER, and a number, and ENTER. The first scanf call read the letter and nothing else. The \n stayed in the input stream. And then the second scanf call, with %d, skipped the \n (because \n is whitespace) and read the number, just like you wanted.

But in your second program you had the inputs in the other order, like this:

printf("Enter number....");
scanf("%d", &number);

printf("Enter letter...");
scanf("%c", &letter);

Now, the user types a number and hits ENTER, and the first scanf call reads the number and leaves the \n on the input stream. But then in the second scanf call, %c does not skip whitespace, so the "letter" it reads is the \n character.

The solution in this case is to explicitly force the whitespace-skipping that %c doesn't do by default. Another little-known fact about scanf is that a space in a format string doesn't mean "match one space character exactly", it means "match an arbitrary number of whitespace characters". So if you change your second program to:

printf("Enter number....");
scanf("%d", &number);

printf("Enter letter...");
scanf(" %c", &letter);

Now, the space character in " %c" in the second scanf call will skip over the \n that was left over after the user typed the number, and the second scanf call should read the letter it's supposed to.

Finally, a bit of editorializing. If you think this is a bizarre situation, if you think the exception to the way %c works is kind of strange, if you think it shouldn't have been this hard to read a number followed by a letter, if you think my explanation of what's going on has been far longer and more complicated than it ought to have been -- you're right. scanf is one of the uglier functions in the C Standard Library. I don't know any C programmers who use it for anything -- I don't believe I've ever used it. Realistically, its only use is for beginning C programmers to get data into their first programs, until they learn other, better ways of performing that task, ways that don't involve scanf.

So my advice to you is not to spend too much time trying to get scanf to work, or learning about all of its other foibles. (It has lots.) As soon as you're comfortable, start learning about the other, better ways of doing input, and leave scanf comfortably behind forever.

Steve Summit
  • 29,350
  • 5
  • 43
  • 68
  • Ah thank you very much for the detailed explanation. I was looking at other solutions I didn't really get where the so-called "whitespace" came from, but I can see why there is a whitespace came from. – Jacques Jul 18 '19 at 15:12
0

Try this

#include <stdio.h>

int main(void) {
    int number;
    char letter;

    printf("Enter letter...");
    scanf("%s", &letter);

    printf("Enter number....");
    scanf("%d", &number);

    printf("Number entered: %d and letter entered: %c.\n", number, letter);
  return 0;
}

If you change the %c to %s then you get the correct output.

S.S. Anne
  • 13,819
  • 7
  • 31
  • 62
Anshu
  • 1,203
  • 1
  • 8
  • 23
  • This is super dangerous, though, because if the user types two or more letters, you'll overwrite memory beyond `letter`. – Steve Summit Jul 18 '19 at 15:53