unexpected behavior of bitwise shifting using gcc
I have a test program like this:
int main()
{
unsigned n = 32;
printf("ans << 32 = 0x%X\n", (~0x0U) << 32);
printf("ans >> 32 = 0x%X\n", (~0x0U) >> 32);
printf("ans << n(32) = 0x%X\n", (~0x0U) << n);
printf("ans >> n(32) = 0x%X\n", (~0x0U) >> n);
return 0开发者_StackOverflow社区;
}
It produces the following output:
ans << 32 = 0x0 ... (1)
ans >> 32 = 0x0 ... (2)
ans << n(32) = 0xFFFFFFFF ... (3)
ans >> n(32) = 0xFFFFFFFF ... (4)
I was expecting (1) and (3) to be the same, as well as (2) and (4) to be the same.
Using gcc version: gcc.real (Ubuntu 4.4.1-4ubuntu9) 4.4.1
What is happening?
Shifting by the size of the type is undefined behavior, according to the C standard, § 6.5.7.3:
6.5.7 Bitwise shift operators
(...) 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.
Your compiler should warn you about this:
$ gcc shift.c -o shift -Wall
shift.c: In function ‘main’:
shift.c:5:5: warning: left shift count >= width of type [enabled by default]
shift.c:6:5: warning: right shift count >= width of type [enabled by default]
If you look at the assembler code gcc is generating, you'll see it is actually calculating the first two results at compilation time. Simplified:
main:
movl $0, %esi
call printf
movl $0, %esi
call printf
movl -4(%rbp), %ecx ; -4(%rbp) is n
movl $-1, %esi
sall %cl, %esi ; This ignores all but the 5 lowest bits of %cl/%ecx
call printf
movl -4(%rbp), %ecx
movl $-1, %esi
shrl %cl, %esi
call printf
精彩评论