4

I noticed some strange behavior with bitshifting with brackets

 #include <stdio.h>

int     main(void)
{
    unsigned char c;
    unsigned char d;

    c = 153;
    c = (c << 7) >> 7;
    printf("%d\n", c);
    d = 153;
    d = (d << 7);
    d = (d >> 7);
    printf("%d\n", d);
}

output:

153
1

I expected c to also have a value of 1... whats going on? Is this undefined?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Theo Walton
  • 1,085
  • 9
  • 24
  • 2
    Take a look at the generated code and what it does. – Some programmer dude Sep 26 '17 at 06:45
  • 1
    Similar to [this](https://stackoverflow.com/questions/33068985/sizeof-an-integer-expression-in-c) question. The expression is evaluated as an integer then stored as a char. Versus being a char at intermediate steps. – matt Sep 26 '17 at 06:50
  • ok that makes sense, lemmi just work with typecasting – Theo Walton Sep 26 '17 at 06:51
  • Please read [Implicit type promotion rules](https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules). – Lundin Sep 26 '17 at 06:54

5 Answers5

4

Bit-shifting a char automatically promotes it to an int. This is why a left shift of 7 bits doesn't chop anything off.

Source: section 6.5.7 of the C standard

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.

Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
2

It gets evaluated as an int, and then stored as a char. And not being a char at intermediate step(s). In other words, when you bit-shift a char, it gets promoted to an int.

Check what the Standard 6.5.7 Bitwise shift operators has to say:

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

gsamaras
  • 71,951
  • 46
  • 188
  • 305
1

According to this online c standard draft, bitwise shift operators promote the parameters to an integer type:

6.5.7 Bitwise shift operators

2 Each of the operands shall have integer type.

3 The integer promotions are performed on each of the operands.

So when you write c = (c << 7) >> 7, the value of c in expression (c << 7) is first converted to an integer value and then shifted. Hence, none of the bits get lost. Shifting them back by >> 7 gives the original value.

When you write d = (d << 7), in contrast, the bits get lost once re-assigning the (integral) result back to d, which is an unsigned char and cannot hold therefore the "higher" bits of the integral value.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
1

153 in binary representation is 10011001. The operands of bitwise shift operator shall have type int, otherwise integer promotion will take place.

For statement

 c = (c << 7) >> 7; 

c is promoted to integer and assuming 4 bytes for integer representation, c will be 00000000 00000000 00000000 10011001 in binary. So the expression

c = (c << 7) >> 7;   // Left and right shift operator will nullify the effect of each other. 

will have the effect of the expression

c = c; 

In case of

d = (d << 7);
d = (d >> 7);  

After the first statement d will have the value (in binary) 10000000. After second statement d will have value (in binary) 00000001.

haccks
  • 104,019
  • 25
  • 176
  • 264
0

Following what this comment suggests, we take a look at the code that is generated on the platform x86 for the expression c = (c << 7) >> 7:

movzbl  31(%esp), %eax ;eax = c
sall    $7, %eax ;left shift
sarl    $7, %eax ;right shift
movb    %al, 31(%esp) ;c = al (one byte)

The contents of c are loaded into a 32-bit register (eax) and both shifts are performed on this register. Finally, the least significant byte of this register (i.e. al) is assigned to the variable c.

In short, both shifts are evaluated as if the operands were 32-bit wide.

JFMR
  • 23,265
  • 4
  • 52
  • 76