6
char byte = 0xff;
printf("%lu\n", sizeof(byte)) // Output is '1'
printf("%x\n", byte); // Output is 'ffffffff'

If the size of byte is only one byte, then why does printf() behave as if it is four bytes?

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
Cory Klein
  • 51,188
  • 43
  • 183
  • 243

4 Answers4

15

Formally, your program exhibits undefined behavior: %x format specification expects an argument of type unsigned int, but you are passing an int, as explained below (hat tip @R). This is harmless in practice on modern two's-complement machines, since int and unsigned have compatible bit layouts. But again, technically, this is undefined behavior and it would be a good idea to fix it, as in printf("%x\n", (unsigned)byte);.

The rules for passing parameters to variadic functions state that all integral types smaller than int get promoted to int. Otherwise, how would printf know, upon seeing %x, whether to grab one byte or four bytes off the stack? From the standard:

5.2.2p7 :
When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg(18.10)... If the argument has integral or enumeration type that is subject to the integral promotions(4.5), or a floating point type that is subject to the floating point promotion(4.6), the value of the argument is converted to the promoted type before the call.

This is how your char turns into an int. It's unspecified whether char is signed or unsigned, but apparently, on the platform you use it's a signed type. So it gets sign-extended when promoted to int. 0xff is (char)-1, and 0xffffffff is (int)-1.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Do you happen to have the Standard reference to those rules? – Mike Kinghan Jul 24 '13 at 21:38
  • 2
    5.2.2p7 When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.10)... If the argument has integral or enumeration type that is subject to the integral promotions (4.5), or a floating point type that is subject to the floating point promotion (4.6), the value of the argument is converted to the promoted type before the call. – Igor Tandetnik Jul 24 '13 at 21:46
  • 1
    This answer is not correct. The issue is that the program is invoking UB by passing the wrong type/value to `printf`. – R.. GitHub STOP HELPING ICE Jul 24 '13 at 23:33
  • While this UB is "harmless" in the sense that it's not formatting your disk or making monkeys fly out of your nose, it's still harmful in the sense that it's printing the wrong value. – R.. GitHub STOP HELPING ICE Jul 25 '13 at 02:57
3

I think it's due to integer promotion

A good blog post on this concept: http://www.idryman.org/blog/2012/11/21/integer-promotion/

Shumail
  • 3,103
  • 4
  • 28
  • 35
2

You have invoked Undefined Behavior by passing the wrong argument type to printf. The %x specifier requires an argument of type unsigned int, but you passed (due to default promotions) a signed int. This is arguably valid if the value of the signed int argument is non-negative, but on your system, plain char happens to be a signed type, so byte contains the result of applying an implementation-defined conversion to 0xff; the usual result of this conversion is -1.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
1

A char is signed 8-bit. The "%x\n" format says to print an integer. So the value of byte is sign extended to an integer. Since a char of 0xff is, in that context, an 8-bit value of -1, printf is just printing the hex integer value of a -1, which is ffffffff.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • 2
    On this platform, plain `char` is a signed 8-bit quantity. The C standard explicitly says that whether plain `char` is signed or unsigned is an implementation-defined detail. – Jonathan Leffler Jul 24 '13 at 21:51