views:

245

answers:

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.

+17  A: 

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.

Oli Charlesworth
@Oli, 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
@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."
Oli Charlesworth
@Oli, oops, right. So I'll integrate that in my answer, too :)
Jens Gustedt
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
+3  A: 

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
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.
Oli Charlesworth
@Oli Thanks for the correction.
Sinan Ünür
Thank you so much Oli and Sinan.I've understood the logic..
Brite Roy
A: 

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
+1  A: 

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

Mike
Thanku Mike sir for sharing this information.You all have great knowledge.
Brite Roy
+4  A: 

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
Thanku Jens sir for ur answer.
Brite Roy