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
tosigned 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.
精彩评论