72

I am trying to print char as positive value:

char ch = 212;
printf("%u", ch);

but I get:

4294967252

How I can get 212 in the output?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Anton
  • 1,051
  • 1
  • 9
  • 21

6 Answers6

45

Declare your ch as

unsigned char ch = 212 ;

And your printf will work.

  • 15
    Even with `ch` changed to `unsigned char`, the behavior of the code is not defined by the C standard. This is because the `unsigned char` is promoted to an `int` (in normal C implementations), so an `int` is passed to `printf` for the specifier `%u`. However, `%u` expects an `unsigned int`, so the types do not match, and the C standard does not define the behavior. – Eric Postpischil Apr 01 '13 at 13:32
  • 5
    Your comment is incorrect. The C11 standard states that the conversion specifier must be of the same type as the function `argument` itself, not the promoted type. This point is also specifically addressed in the description of the `hh` length modifier: "the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing" – andypea Apr 13 '14 at 23:10
  • Only worked after explicit cast to ` unsigned int` when passed to `prtinf()`. Adding -std=c11 to the gcc-4.8.4 command to enforce the standard had no effect. – ypx Oct 23 '15 at 09:42
  • @EricPostpischil What exactly does %d tells ? And what does the specifier %u tells ? – Suraj Jain Dec 28 '16 at 07:49
  • 2
    @andrew.punnett: The function argument is the promoted value; it has the type that results from the integer promotions, per C 2011 6.5.2.2 6. In the case of `hh`, yes, the standard tells us that the type before promotion may be a `signed char` or `unsigned char`. It means that the `ch` will still be promoted to an `int`, but the conversion `%hhu` expects that. However, that is not relevant to this situation where `hh` is not present in `printf(%u, ch)`. – Eric Postpischil Dec 29 '16 at 12:33
  • @SurajJain: `%d` is used for printing an `int` argument in decimal. `%u` is used for printing an `unsigned int` argument in decimal. – Eric Postpischil Dec 29 '16 at 12:35
  • @EricPostpischil if i do unsigned char a = -2 and signed char a = -1 , would be the underlying bit pattern same , what would it be ? – Suraj Jain Dec 29 '16 at 12:44
40

This is because in this case the char type is signed on your system*. When this happens, the data gets sign-extended during the default conversions while passing the data to the function with variable number of arguments. Since 212 is greater than 0x80, it's treated as negative, %u interprets the number as a large positive number:

212 = 0xD4

When it is sign-extended, FFs are pre-pended to your number, so it becomes

0xFFFFFFD4 = 4294967252

which is the number that gets printed.

Note that this behavior is specific to your implementation. According to C99 specification, all char types are promoted to (signed) int, because an int can represent all values of a char, signed or unsigned:

6.1.1.2: If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

This results in passing an int to a format specifier %u, which expects an unsigned int.

To avoid undefined behavior in your program, add explicit type casts as follows:

unsigned char ch = (unsigned char)212;
printf("%u", (unsigned int)ch);


* In general, the standard leaves the signedness of char up to the implementation. See this question for more details.
Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    This answer is muddled and inaccurate. The sentences about sign extension and treating 212 are presented without context, failing to explain where they apply (the earlier sentence applies to a later operation, integer promotion of the `char` when it is used as an argument, while the later sentence applies to an earlier operation, initialization of the `char` with an `int`). – Eric Postpischil Apr 01 '13 at 13:12
  • 1
    The statement that 212 is treated as negative because it is greater than 0x80 is false. First, initializing a signed `char` with 212 causes integer overflow in most C implementations, so the behavior is undefined. In many C implementations, this results in truncation of the two’s complement representation of 212 to eight bits, which causes the bits to be interpreted as a negative value because the 0x80 bit is set, not because the initial value is greater than 0x80 (counterexamples include 0x80 and 0x100). – Eric Postpischil Apr 01 '13 at 13:13
  • 1
    After the initialization of the `char`, it is used as an argument to `printf`. The integer promotions are performed, resulting in a negative `int` being passed to `printf`. Again, many C implementations use two’s complement, so a sign extension is performed, as this answer says. Converting the value to `unsigned` at this point would yield a large positive number. However, this answer fails to explain that the actual behavior is undefined by the C standard because an `int` is being passed for a specifier of `%u`, which expects an `unsigned int`. – Eric Postpischil Apr 01 '13 at 13:14
  • @EricPostpischil Thanks for your comments! I missed the mismatch between the argument and the format specifier. This is now fixed. – Sergey Kalinichenko Apr 01 '13 at 13:51
  • @dasblinkenlight `char` isn’t guaranteed to fit in type `int` since `char` may have been defined to the same behavior as `unsigned char`. Only an integer of the same signedness but smaller integer conversion rank is guaranteed to have a range of values that is a subrange of the range of values of the other type (**section 6.2.5** paragraph 8 of the C99 standard). Therefore, `unsigned char` could theoretically have a range as wide as `unsigned int` due to its signedness and the integer conversion ranks. – Vilhelm Gray Jul 25 '13 at 21:23
  • If I recall correctly, the magnitudes of maximum and minimum values of integer types are never larger than the `unsigned` maximum value of the respective type, so all `char` values can therefore fit in an `unsigned int`. – Vilhelm Gray Jul 25 '13 at 21:24
  • My last comment only applies to character types since `unsigned char` explicitly has no padding bits (**section 6.2.6.2** paragraph 1) and `signed char` occupies the same storage as a `char` (**section 6.2.5** paragraph 5) which may be represented by an `unsigned char`. Since a `signed char` must have one bit dedicated as the "sign bit" all `signed char` values can be converted to `unsigned char` and back without loss. So all `char` values can be converted to `unsigned int`; though you may not be able to differentiate between true positive values and negative values cast to `unsigned`. – Vilhelm Gray Jul 26 '13 at 15:36
  • If I have something like this `int x = 1234` and `char *y = &x` . Binary representation of `1234 ` is `00000000 00000000 00000100 11010010` . My machine is little endian so it reverses it and store in memory `11010010 00000100 00000000 00000000` LSB comes first. Now Main Part . if i use `printf("%d" , *p)`. `printf` will read first byte `11010010`only the output is `-46` but `11010010` is `210` so why does it print `-46` . I am really confused i guess some char to integer promotion is doing something but i don't know. What Is Happening? – Suraj Jain Aug 17 '16 at 10:26
  • @SurajJain please ask a question instead of commenting on an answer. – Sergey Kalinichenko Aug 17 '16 at 10:38
  • @dasblinkenlight I have been restricted to write a question . That is why i am asking here. The ban will be lifted much later. – Suraj Jain Aug 17 '16 at 10:41
  • @dasblinkenlight What Do You Mean By It is treated as nagative ? – Suraj Jain Dec 30 '16 at 06:05
21

There are two bugs in this code. First, in most C implementations with signed char, there is a problem in char ch = 212 because 212 does not fit in an 8-bit signed char, and the C standard does not fully define the behavior (it requires the implementation to define the behavior). It should instead be:

unsigned char ch = 212;

Second, in printf("%u",ch), ch will be promoted to an int in normal C implementations. However, the %u specifier expects an unsigned int, and the C standard does not define behavior when the wrong type is passed. It should instead be:

printf("%hhu", ch);

(For %hhu, printf expects an unsigned char that has, in normal C implementations, been promoted to int.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

In case you cannot change the declaration for whatever reason, you can do:

char ch = 212;
printf("%d", (unsigned char) ch);
Makketronix
  • 1,389
  • 1
  • 11
  • 31
0

The range of char is 127 to -128. If you assign 212, ch stores -44 (212-128-128) not 212.So if you try to print a negative number as unsigned you get (MAX value of unsigned int)-abs(number) which in this case is 4294967252

So if you want to store 212 as it is in ch the only thing you can do is declare ch as

unsigned char ch;

now the range of ch is 0 to 255.

banarun
  • 2,305
  • 2
  • 23
  • 40
0

Because char is by default signed declared that means the range of the variable is

-127 to +127>

your value is overflowed. To get the desired value you have to declared the unsigned modifier. the modifier's (unsigned) range is:

 0 to 255

to get the the range of any data type follow the process 2^bit example charis 8 bit length to get its range just 2 ^(power) 8.

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
Shohanur Rahaman
  • 379
  • 4
  • 14
  • "Because char is by default signed declared..." This is not correct: a `char` is **not** signed by default. The standard says (sec 6.2.5, "Types"): "The implementation shall define `char` to have the same range, representation, and behavior as either `signed char` or `unsigned char`", and there is no mention of a "default". Whether a `char` is signed or unsigned is simply left to the implementation to decide. – phlummox Aug 06 '23 at 12:45