4
 unsigned short int i = 0;
 printf("%u\n",~i);

Why does this code return a 32 bit number in the console? It should be 16 bit, since short is 2 bytes.

The output is 4,294,967,295 and it should be 65,535.

Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
Greyshack
  • 1,901
  • 7
  • 29
  • 48

4 Answers4

5

%u expects an unsigned int; if you want to print an unsigned short int, use %hu.

EDIT

Lundin is correct that ~i will be converted to type int before being passed to printf. i is also converted to int by virtue of being passed to a variadic function. However, printf will convert the argument back to unsigned short before printing if the %hu conversion specifier is used:

7.21.6.1 The fprintf function
...
3 The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not %), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream.
...
7 The length modifiers and their meanings are:
...
h Specifies that a following d, i, o, u, x, or X conversion specifier applies to a short int or unsigned short int argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to short int or unsigned short int before printing); or that a following n conversion specifier applies to a pointer to a short int argument.

Emphasis mine.

So, the behavior is not undefined; it would only be undefined if either i or ~i were not integral types.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • This works. What would be the equivalent for unsigned long int then? and long long int? – Greyshack Nov 25 '14 at 14:37
  • 1
    @Greyshack For a `long unsigned int`, use `%lu`; for a `long long unsigned int` use `%llu`. – fuz Nov 25 '14 at 14:42
  • `~i` is however never of type `unsigned short int`. I believe `printf("%hu\n",~i);` invokes undefined behavior because you are passing an `int` to a printf which expects `unsigned short`. – Lundin Nov 25 '14 at 15:05
  • @Greyshack: There's a nice table of the various options here: http://en.cppreference.com/w/c/io/fprintf – Bill Lynch Nov 25 '14 at 15:12
  • @Lundin: Not really. `printf()` is a variadic function so even if you passed just `i` it would be promoted. – Bill Lynch Nov 25 '14 at 15:13
4

When you pass an argument to printf and that argument is of integer type shorter than int, it is implicitly promoted to int as per K&R argument promotion rules. Thus your printf-call actually behaves like:

printf("%u\n", (int)~i);

Notice that this is undefined behavior since you told printf that the argument has an unsigned type whereas int is actually a signed type. Convert i to unsigned short and then to unsignedto resolve the undefined behavior and your problem:

printf("%u\n", (unsigned)(unsigned short)~i);
fuz
  • 88,405
  • 25
  • 200
  • 352
  • The argument is already of type int when passed to printf, because of integer promotion caused by `~`. `(int)~i` is 100% equivalent to `~i`. Also the cast `(unsigned)(unsigned short)~i` is nonsense, simply do `(unsigned int)~i`. – Lundin Nov 25 '14 at 15:06
  • @Lundin I have to correct you. `(unsigned)(unsigned short)~i` is not equal to `(unsigned int)~i`. For instance, on a platform with a 16 bit `short` and a 32 bit `int` with `i == 1`, `(unsigned)(unsigned short)~i` has the value `0xfffe` whereas `(unsigned int)~i` has the value `0xfffffffe`. – fuz Dec 15 '14 at 09:49
  • @FUZxxl But it is still nonsense, why would you ever want the unsigned short to be implicitly promoted to a signed type, then invert that signed type, and then trucate that result? – Lundin Dec 21 '14 at 19:51
  • @Lundin Because `int` might have the same size as a `short`. If we used `printf("%d\n", (unsigned short)~i)`, the result would come out incorrectly where `short` and `int` have the same size. – fuz Dec 21 '14 at 23:47
4

N1570 6.5.3.3 Unary arithmetic operators p4:

The result of the ~ operator is the bitwise complement of its (promoted) operand (that is, each bit in the result is set if and only if the corresponding bit in the converted operand is not set). The integer promotions are performed on the operand, and the result has the promoted type. ...

Integer type smaller than int are promoted to int. If sizeof(unsigned short) == 2 and sizeof(int) == 4, then resulting type is int.

And what's more, printf conversion specifier %u expects unsigned int, so representation of int is interpreted as unsigned int. You are basically lying to compiler, and this is undefined behaviour.

user694733
  • 15,208
  • 2
  • 42
  • 68
  • The only good answer, which didn't fail to point out that the `~` operator has already promoted the variable to type `int` before it is passed to printf. `printf("%d\n",~i);` would have worked just fine and that is _not_ because of some printf argument promotion! – Lundin Nov 25 '14 at 15:03
  • This only tells half of the story; yes, arguments to `printf` are promoted, but then `printf` will *convert them to the appropriate type* based on the conversion specifier. See my edited answer above. – John Bode Nov 25 '14 at 16:27
3

It's because the arguments to printf() are put into the stack in words, as there is no way inside printf to know that the argument is short. Also by using %u format you are merely stating that you are passing an unsigned number.

deStrangis
  • 1,912
  • 1
  • 18
  • 25
  • this is undefined behaviour, as the expression `(~i)&0xffff` has type `int` whereas `%u` expects an argument of type `unsigned int`. – fuz Nov 25 '14 at 14:29
  • But he is not passing a short, he is passing an int. – Lundin Nov 25 '14 at 15:09