I recently noticed a (weird) behavior when I conducted operations using shift >>
<<
!
To explain it, let me write this small run
In the first example:
a
is converted to an int
, shifted left, then right and then converted back to usigned char
. This will result to a=5
obviously.
In the second example:
b
is converted to int
, shifted left, then converted back to unsigned char
.b
is converted to int
, shifted right, then converted back to unsigned char
.The difference is that you lose information in the second example during the conversion to unsigned char
The shift operations would do integer promotions to its operands, and in your code the resulting int
is converted back to char
like this:
// first operation
a = ((a<<7)>>7); // a = (char)((a<<7)>>7);
// second operation
b <<= 7; // b = (char) (b << 7);
b >>= 7; // b = (char) (b >> 7);
Quote from the N1570 draft (which became the standard of C11 later):
6.5.7 Bitwise shift operators:
- Each of the operands shall have integer type.
- The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
And it's supposed that in C99 and C90 there are similar statements.
Detailed explanation of the things going on between the lines:
Case a:
a = ((a<<7)>>7);
, a<<7
is evaluated first.int
. int
.a
gets promoted to type int
, still containing the value 0x05. The 7
literal was already of type int
so it doesn't get promoted.int
by 7, you get 0x0280. The result of the operation is of type int
.int
is a signed type, so had you kept shifting data further, into the sign bits, you would have invoked undefined behavior. Similarly, had either the left or the right operand been a negative value, you would also invoke undefined behavior.0x280 >> 7;
. No promotions take place for the next shift operation, since both operands are already int. Case b:
b <<= 7;
is equivalent to b = b << 7;
.b
gets promoted to an int
. The result will again be 0x0280.0x80
.b
again gets promoted to an int, containing 0x80.Good advice:
stdint.h
rather than the primitive default types in C.A better, safer way to write your program would have been:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t a=0x05;
uint8_t b=0x05;
uint32_t tmp;
// first operation
tmp = (uint32_t)a << 7;
tmp = tmp >> 7;
a = (uint8_t)tmp;
// second operation
tmp = (uint32_t)b << 7;
tmp = tmp >> 7;
b = (uint8_t)tmp;
printf("a=%X b=%X\n", a, b);
return 0;
}