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

前端 未结 5 496
一整个雨季
一整个雨季 2021-01-05 21:27
#include
int main(void)
{
  char c = 0x80;
  printf(\"%d\\n\", c << 1);
  return 0;
}

The output is -256 in this

相关标签:
5条回答
  • 2021-01-05 21:45

    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 讨论(0)
  • 2021-01-05 21:48

    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.

    0 讨论(0)
  • 2021-01-05 21:50

    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.

    0 讨论(0)
  • 2021-01-05 21:52

    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
    0 讨论(0)
  • 2021-01-05 21:56

    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).

    0 讨论(0)
提交回复
热议问题