10

I have tried scanf("%u",&number) and I have entered negative number the problem is when I printf("%d",number) I get the negative number. I thought this will prevent me from reading negative number. Are scanf("%d",&number) and scanf("%u",&number) the same thing really ? or is it only for readibility.

Am I doing something called undefined behavior so ?

EDIT:

From Wikipedia I read this:

%u : Scan for decimal unsigned int (Note that in the C99 standard the input value minus sign is optional, so if a minus sign is read, no errors will arise and the result will be the two's complement of a negative number, likely a very large value.

It is a little bit confusing reading SO answers and above. Can someone make it more clear ?

Community
  • 1
  • 1
rondino
  • 395
  • 2
  • 3
  • 10

2 Answers2

2

Yes, it's undefined behavior, either way.

Considering variable number is of type unsigned, %d in printf() expects an argument of signed int type, passing an unsigned type is UB.

OTOH, if number is signed type, using %u for scanning is UB in first place.

As you might have expected

[...] prevent me from reading negative number

format specifiers are not there to prevent improper input. If the format specifier does not match the supplied argument, it invokes undefined behavior.

Quoting C11, annex J.2, scenarios invoking UB,

The result of a conversion by one of the formatted input functions cannot be represented in the corresponding object, or the receiving object does not have an appropriate type

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • `printf("%u",-1)` is it also UB ? – rondino Jul 31 '16 at 13:32
  • 1
    @rondino technically, it is. [see this](http://stackoverflow.com/q/4664100/2173917) – Sourav Ghosh Jul 31 '16 at 13:43
  • @rondino: Yes, `printf("%u", -1)` has undefined behavior. `-1` is of type `int` and `%u` requires an argument of type `unsigned int`. Corresponding signed and unsigned types can be interchangeable in some contexts, but *only* for values that are representable in both types. – Keith Thompson Aug 01 '16 at 15:42
2

As explained in detail by Sourav Ghosh, using formats that are inconsistent with the actual types passed is a potential problem. Yet for this particular case, on current PC architectures, this is not really a problem, as neither int nor unsigned int have trap representations.

You can scan a negative number with scanf("%u", &number);. It will be negated in the destination type, namely unsigned int, with the same bitwise representation as the negative number in a signed int, for two's complement representation which is almost universal on current architectures.

scanf converts %u by matching an optionally signed decimal integer, whose format is the same as expected for the subject sequence of the strtoul function with the value 10 for the base argument. The corresponding argument shall be a pointer to unsigned integer.

The strtol, strtoll, strtoul, and strtoull functions convert the initial portion of the string pointed to by nptr to long int, long long int, unsigned long int, and unsigned long long int representation, respectively. First, they decompose the input string into three parts: an initial, possibly empty, sequence of white-space characters (as specified by the isspace function), a subject sequence resembling an integer represented in some radix determined by the value of base, and a final string of one or more unrecognized characters, including the terminating null character of the input string. Then, they attempt to convert the subject sequence to an integer, and return the result.

If the value of base is between 2 and 36 (inclusive), the expected form of the subject sequence is a sequence of letters and digits representing an integer with the radix specified by base, optionally preceded by a plus or minus sign, but not including an integer suffix.

... If the subject sequence has the expected form and the value of base is between 2 and 36, it is used as the base for conversion, ascribing to each letter its value as given above. If the subject sequence begins with a minus sign, the value resulting from the conversion is negated (in the return type).

If the type of number is unsigned int, behavior is defined and the negative value is parsed and stored into number using the unsigned negation semantics. Printing this value with printf("%d", number); is at best implementation defined, but again, on current PC architectures, will print the negative number that was originally parsed by scanf("%u", &number);

Conclusion: although it seems harmless, it is very sloppy to use int and unsigned int interchangeably and to use the wrong formats in printf and scanf. As a matter of fact, mixing signed and unsigned types in expressions, esp. in comparisons is very error prone as the C semantics for such constructions are sometimes counter-intuitive.

Community
  • 1
  • 1
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • "*It will be negated in the destination type, named `unsigned`, with the same bitwise representation as the negative number in a `signed int`.*" -- That's true only for 2's-complement representation (which is almost universal but not guaranteed). – Keith Thompson Aug 01 '16 at 15:40
  • @KeithThompson: correct. I updated the answer. I cannot wait to see these obsolete possibilities to be removed from a future version of the C Standard. – chqrlie Aug 01 '16 at 16:05