0

Here is my C code:

#include "stdio.h"

int main()
{
  int minx, x;

  printf("Enter two ints: ");
  scanf( "%d-%d", &minx, &x);

   printf("You wrote: minx is %d x is %d", minx, x);
}

When the input is 5-3 or 5- 3 the output is You wrote: minx is 5 x is 3 which makes sense. However, when the input is 5 -3 or 5 - 3 or 6 -4 the output is You wrote: minx is 5 x is 8. I expected - to skip white spaces, so I expected minx to be 5 and x to be 3,6 and 4 for the other input. This also happens when - in %d-%d changed to ?, *, + even with the same inputs. I know it is probably because of that space after the first int. Here it says only three format specifiers do not skip white space — Whitespace before %c specification in the format specifier of scanf function in C. Did I get this wrong? Why - does not skip leading space here? What is the actual problem here and what is the cause of it? Why is it 8? What other operators or chars can lead to similar problems?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
BoraKurucu
  • 39
  • 1
  • 1
  • 8
  • 4
    The comments here have addressed most of the issues. The outstanding one is why you get the value of `8` for `x`. This is because `scanf` is failing to read two integers, so no value is assigned to `x` … and `8` happens to be the value that memory has at program start-up. If you initialise it to any other value (say `int minx = -1, x = -2;`) then you will get *that* value instead of `8`. – Adrian Mole Oct 15 '19 at 20:26
  • I've clarified the Q&A referenced in the question to clarify that literal characters in a format string are not counted as conversion specifications and do not skip leading white space. If you have a `scanf()` format string containing `"ABC%dDEF"`, then `A`, `B` and `C` must appear in consecutive characters (no white space); the number may be preceded by any amount of white space, including newlines, and include a sign (but no white space); the character `D` must terminate the numeric input, and be followed by `E` and `F` with no spaces. Note that you cannot readily tell whether `DEF` matched. – Jonathan Leffler Oct 16 '19 at 04:04

2 Answers2

5

Let's look at your scanf format specifier "%d-%d" in detail:

  • %d skip whitespace if necessary, then read an integer
  • - match a literal '-' character
  • %d skip whitespace if necessary, then read an integer

So the inputs 5-3 and 5- 3 both work just fine. But when the input is 5 -3 (or anything else with a space before the -) the parse fails, because scanf does not immediately see the - it expects.

If that's not what you expected, or not what you want, or if that doesn't make sense, or if that's not how you'd like scanf to work, I'm afraid that's too bad: scanf works the way it works.

How can you fix this? That depends in part on why you included the - character in your format string in the first place.

  • You can use %d%d or %d %d, which will simply read two integers (separated by at least one whitespace character). If there's a - character preceding the second integer, that integer will be read as negative.
  • You can use %d -%d, which will skip (arbitrary) whitespace before it tries to match the - character.
  • You can use two separate scanf calls.
  • If you do continue to use scanf, you really need to check its return value so that your program can detect the case that the expected inputs were not matched.
  • Finally, you can use something other than scanf.

My recommendation to you depends on the ultimate purpose of this program.

  • If it's just for learning, then minimize the amount of time you spend fussing with the way your program does input at all. For example, if you need to read an integer, use one %d. As long as you can get the numbers you need into your program (so that you can test the rest of your program), you're fine. If there are things you can type that cause scanf to get confused, just don't worry about it, just don't type those things. Don't try to do anything fancy — that's not what scanf is for.
  • If this is a "real" program, that does have to accept arbitrary user input, or input with a specific syntax (like with that - in the right place), and if you need to deal gracefully with incorrect input, printing appropriate errors, and not reading the wrong values or getting confused — then run, do not walk, away from scanf, and never use it again. It is effectively impossible to write a program that performs high-quality input using scanf. Not even a C expert can do it. It's simply not worth it. You will spend five times as long, and get an inferior result, than if you simply abandoned scanf and read your input a line at a time using fgets or the like, then parsed the input line (perhaps even using sscanf — but again, check its return value).

Addendum: It's true, all format specifiers — with three exceptions (%c, %[…] scan sets, and %n) — skip whitespace before beginning their work. But format specifiers are things that begin with %. Literal characters in the format string must match exactly, and there's no implicit whitespace skipping for them. If you want to skip whitespace at spots in the input other than before the % format specifiers that do it, you can include a literal whitespace character (usually a single space) in your format string.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • 1
    I dunno whether it's worth mentioning that `%d -%d` would allow `-12--14` as valid input, and switching to `%u` doesn't help (negative signs are recognized per [`fscanf()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html) — _`u` Matches an optionally signed decimal integer, …_) – Jonathan Leffler Oct 16 '19 at 02:32
2

You need to check the return value of scanf. You should be doing this, in general, for every library function you call that has a return value.

In your case, scanf would return 2 (meaning two output parameters were set) for valid input. If you get any other return value, the input does not match the format you specified, and you should ignore the contents of your output parameters minx and x.

craig65535
  • 3,439
  • 1
  • 23
  • 49