Warm tip: This article is reproduced from serverfault.com, please click

Why does printing a character variable with %d give a negative value in c?

发布于 2020-11-28 10:58:32

I tried the following piece of code, expecting the output to be positive 64:

char val = 0x80; 
printf("%d",val>>1);

My understanding of what happens is(please correct me if i'm wrong as i probably am):

  1. Referring to the ASCII table, there is no mapping of 0x80 to any character so i assume this is stored as an unsigned integer.
  2. This is represented as 1000 0000 in bitwise format, so a right shift of 1 would result in 0100 0000
  3. When printed as an integer value, this will then show as positive 64.

However it shows -64.

In contrast:

char val = 0x40; 
printf("%d",val>>1);

gives positive 32.

Is the value implicitly converted to a signed integer in the first case and not in the second?

Questioner
kiblykat
Viewed
0
Eric Postpischil 2020-11-28 19:42:38

Your C implementation uses an eight-bit signed char. (The C standard permits char to be signed or unsigned.) In char val = 0x80;, a char cannot represent the value you initialize it with, 128. In this case, the value 128 is converted to char which, per C 2018 6.3.1.3 3, yields either an implementation-defined value or a trap. Your implementation likely produces −128. (This is a common result because 128 in binary is 10000000, and converting an out-of-range result to an eight-bit two’s complement integer often simply reinterprets the low eight bits of the value as eight-bit two’s complement. In two’s complement, 10000000 represents −128.)

So val>>1 asks to shift −128 right one bit. Per C 2018 6.5.7 5, shifting a negative value right yields an implementation defined value. Producing −64 is a common result.

(In detail, in val>>1, val is automatically promoted from char to int. It has the same value, −128. However, with a 32-bit int, it would then be represented as 11111111111111111111111110000000 instead of 10000000. Then shifting right “arithmetically,” which propagates the sign bit, yields 11111111111111111111111111000000, which is −64, the result you go. Some C implementations might shift right “logically,” which sets the sign bit to zero, yielding 01111111111111111111111111000000. In this case, the printf would show “2147483584”, which is 231−64).

Whether ASCII has any character with code 0x80 is irrelevant. The C rules apply to the values involved, regardless of what character encoding scheme is used.