开发者

If char c = 0x80, why does printf("%d\n", c << 1) output -256?

#include<stdio.h>
int main(void)
{
  char c = 0x80;
  printf("%d\n", c << 1);
  return 0;
}

The output is -256 in this case. If I write c << 0 then the outp开发者_如何转开发ut is -128.

I don't understand the logic behind this code.


char may be signed on your platform, in which case 0x80 represents -128 (assuming two's complement).

When a char is used as an operand with the << operator, it is promoted to int (still -128). So when you apply the left-shift, you get -256. Technically, shifting negative values is implementation-defined undefined, but what you see is typical behaviour.


Already your starting point is problematic:

char c = 0x80;

If (as seemingly in your case) char is a signed type, you are assigning the integer constant 128 to a type that is only guaranteed to hold values up to 127. Your compiler then may choose to give you some implementation defined value (-128 in your case I guess) or to issue a range error.

Then you are doing a left shift on that negative value. This gives undefined behavior. In total you have several implementation defined choices plus undefined behavior that determine the outcome:

  • signedness of char
  • the choice of how to convert 128 to signed char
  • the width of char
  • the sign representation of int (there are three possibilities)
  • the choice on how to implement (or not) left shift on negative int

It may be a good exercise for you to look up all these case an to see what the different outcomes may be.

In summary some recommendations:

  • choose an appropriate constant to initialize a variable
  • don't do arithmetic with plain char
  • don't do left shift on signed types


c is assigned 0x80. Assuming 8-bit bytes, its value in binary representation, is 10000000. Apparently, on your platform, char is a signed type. So, 0x80 (i.e. 10000000) corresponds to -128.

When << is applied to a char value, it is promoted to int and the sign is preserved. So, when shifted once to the left, with 32-bit integers, it becomes 11111111111111111111111100000000 (two's complement) which is -256.


Just a side-note. From a bottom up perspective, bit-wise shifting (and masking) is based on an architecture's word-length (expressed in bits). The length of a word, varies from architecture to architecture.

See this Wiki page for word lengths by architecture

If one knows the word length of the target architecture, one can use bit-shifting to multiply, and divide (in some cases), faster than using operands.

See this Wiki page for interesting diagrams of bit-shifting

Since bit-shifted code is architecture dependent, one cannot assume a specific piece of bit-shifted code will work the same way from architecture to architecture. However, once one is familiar with the idea of different word lengths for different architectures, bit-shifting becomes less mysterious and more predictable.

Thankfully, today we have 8, 16, 32, and 64 bit word lengths, and exclusively 8 bit character lengths. In the days of ancient computing, an architecture might have a 12, or a 15, or a 23 bit word length (etc., ad nauseum).


I wonder why your compiler do not complain with a warning that 0x80 does not fit in char, which on your platform can represent only values from -0x80 to 0x7F.

Try this piece of code:

 #include <stdio.h>
 #include <limits.h>
 #include <stdlib.h>

 int main() {
      printf("char can represent values from %d to %d.\n", CHAR_MIN, CHAR_MAX);
      return EXIT_SUCCESS;
 }

Your situation is called OVERFLOW.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜