5
#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 output is -128.

I don't understand the logic behind this code.

Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
Brite Roy
  • 425
  • 3
  • 9
  • 28

5 Answers5

18

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.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I don't think your statement is about `int` promotion is correct. Shift operators don't do promotion, the result type is the type on the left. – Jens Gustedt Oct 23 '10 at 14:38
  • @Jens: To be fair, I only have the C99 standard in front of me. But it says in section 6.5.7: "The integer promotions are performed on each of the operands." – Oliver Charlesworth Oct 23 '10 at 14:43
  • I didn't realize left-shifting negative values was undefined, but apparently it is. From §6.5.7.4 of the C99 standard, regarding the expression `E1 << E2`: "If `E1` has a signed type and nonnegative value, and `E1 × 2^E2` is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined." – Adam Rosenfield Oct 24 '10 at 01:35
6

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
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Thanku Jens sir for ur answer. – Brite Roy Oct 23 '10 at 12:04
  • The first paragraph of the explanation is wrong and can mislead someone inexperienced. "char c = 0x80;" is fine just like "int c = 0xFFFFFFFF" is fine, but the part that isn't fine is assuming 0x80 will be 128 when char may be signed (range of -128 to 127, not 0 to 255) – B. Nadolson Jun 27 '15 at 03:20
  • @B.Nadolson, I don't think that it is misleading. The constant `0x80` is of type `int` and has value `128`. This type and value on the RHS is determined first, and then that type and value is converted to fit the type on the LHS. Trying to initialize a possibly signed variable with a value that doesn't fit the type is a semantic error. – Jens Gustedt Jun 27 '15 at 07:08
  • C99 standard (6.4.4.1) says you are right. Although it is common to represent a negative number with hex. Seem I can't reverse the vote. – B. Nadolson Jun 28 '15 at 20:03
  • @B.Nadolson, never mind for the vote. Yes, this is a common error. But in many cases the error is in fact in the choice of the type, as in this example. – Jens Gustedt Jun 28 '15 at 23:23
3

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.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • The promotion to `int` is nothing to do with `printf` in this instance; it's due to the `<<`. Also, that's not the representation of -256 in either one's or two's complement. – Oliver Charlesworth Oct 22 '10 at 23:36
1

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

Michael
  • 1,340
  • 1
  • 11
  • 7
0

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.

Vovanium
  • 3,798
  • 17
  • 23