Reputation:
in this code:
char t = 200;
printf("%u \n",t);
printf("%u \n",(unsigned char)t);
output is this:
4294967240
200
Why first printf
prints some random value? Was %u specifier alone not enough to interpret the number as unsigned? and print 200?
Why does the cast help in the second case? What is happening under the hood?
Upvotes: 1
Views: 192
Reputation: 2505
Inside of a variable argument function, all integral types that are passed in are automatically promoted to the longest integral type that the system supports. This means that single byte char
types are promoted to 32 bit or 64 bit types on modern x86 computers when passed into a function like printf
. I know for a fact that GCC's char
(and evidently whatever compiler you're using, too) defaults to being signed, so your value of 200 is actually -56 in a 2's compliment signed integer scheme.
Let's first take a look at why first casting it to an unsigned char
prints 200 like expected.
The binary representation for the value 200 is 11001000
. That value is 8 bits wide, and you set a signed 8 bit type to this value.
In 2's compliment arithmetic, that same binary value actually represents -56.
Casting that byte to an unsigned type would still be 11001000
, but it would change the meaning of it back to 200.
If you promote that to a 32 bit value using unsigned arithmetic, that would become 00000000 00000000 00000000 11001000
That 32 bit value still represents the value 200, and printing it as an unsigned value will indeed show 200.
Now let's look at the case where you aren't casting it.
The binary representation for the value 200 is 11001000
. That value is 8 bits wide, and you set a signed 8 bit type to this value.
In 2's compliment arithmetic, that same binary value actually represents -56.
If you promote that to a 32 bit value using 2's compliment arithmetic, that would become the binary value 11111111 11111111 11111111 11001000
That 32 bit value still represents -56 in 2's compliment arithmetic. However, by printing it as an unsigned integer, you've made the compiler reinterpret a 32 bit signed value as a 32 bit unsigned value. For any non-negative signed value, this works as expected. However, all of those leading 1
s in the 2's compliment negative value now represent a very large unsigned type.
If I punch that 32 bit representation of -56 into a binary-to-unsigned-decimal converter, I get the value 4,294,967,240 - which is the value you said you were getting by calling printf("%u", -52);
In the case of printf
, you're really expected to know how to treat the data that's being passed in. All that printf
sees in the argument list is a long binary blob; it doesn't even know how many arguments there are. The format string is supposed to tell printf
how to interpret that binary blob of data. There's absolutely no type checking that printf
can perform. If you tell it to read an integer from the binary blob, it will try to render the next sizeof(int)
bytes as an integer, even if the binary data is actually intended to represent a float
.
In this case, you've passed in a single signed char
to printf
, which caused the binary blob to contain contain a signed 32 bit integer, and you've told printf
that it's actually an unsigned 32 bit integer. There's no way for printf
to tell that it should actually be a signed char
.
Upvotes: 0
Reputation: 106022
The C standard doesn't specify whether ordinary char
is a signed
or an unsigned
type. Some compilers treat it as a signed
while others treat it as unsigned
type.
It seems that char
is signed
by default on your implementation. signed char
can hold value between -128 to 127
only.
Why first printf prints some random value?
The reason that you are getting random value is because of undefined behavior of the program.
Was
%u
specifier alone not enough to interpret the number as unsigned? and print 200?
No. No type promotion inside printf
function.
C11: 7.29.2 p(9):
If a conversion specification is invalid, the behavior is undefined.335)
If you declare
double f = 5.7;
and use %d
to print f
, then you might expect that you will get an int
value.
printf("%d", f) // Undefined behavior.
but it will invoke undefined behavior.
Why does the cast help in the second case? What is happening under the hood?
It helps in nothing. Once you get undefined behavior, all bets are off.
Upvotes: 7
Reputation: 5651
That is because char
is a byte. 200 in binary is 11001000
. int
is at least 16 bits wide. When you cast %u
, it becomes unsigned int
.
4294967240 in binary is 11111111111111111111111111001000
.
11111111-11111111-11111111-11001000
If you look at the last 2 bytes, that is the same as 200 in binary.
This is assuming OP is using x86 system.
Upvotes: -1
Reputation: 10867
There are a few things going on here.
char
on your system is signed
by default (this is implementation-defined, it just happens to be so on your system)the above means it can only hold values -128 to +127 (assuming x86 architecture), so this:
/*signed*/ char t = 200;
... is actually interpreted as this:
char t = (signed char)200; /* actual value: -56 */
so that's why t
is negative.
t
to printf()
, it gets promoted to a signed int
, whilst maintaining its value (-56
)
printf
being one of them; see note below)printf()
is expecting an unsigned int
(because you're using %u
and not %d
), and thus interprets the integer as that (signed -56
and unsigned 4294967240
have the same representation in x86)t
to unsigned char
actually does two things:
-56
back to +200
(since -56
cannot be represented by a signed char
); this is because of the unsigned char
+200
is extended to fill an unsigned int
. This is because printf
is a variadic function(* note: for an explaination and examples of variadic functions in C, see this WP entry: http://en.wikipedia.org/wiki/Variadic_function#Example_in_C)
(* note2: I've simplified some things here for the same of brevity and understanding; C standard does not, for example, require that char
is 8-bit --- but it is in most modern systems)
Upvotes: 2