移位运算符规则导致的bug

CPrimerPlus第15章练习题第二题C语言移位运算符的规则引起的bug_#include

题目不难,很快程序就写好了

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
int Trans2Int(char *); //将字符串的二进制数转化成整数
void PrintBase2(int);//将整数以二进制形式打印
int main(int argc, char * argv[]){
        int num1, num2;
        num1 = Trans2Int(argv[1]);
        num2 = Trans2Int(argv[2]);
        printf("num1 = ");PrintBase2(num1);
        printf(" num2 = ");PrintBase2(num2);putchar('\n');
        printf("~num1 = ");PrintBase2(~num1);
        printf(" ~num2 = ");PrintBase2(~num2);putchar('\n');
        printf("num1 & num2 = ");PrintBase2(num1 & num2);putchar('\n');
        printf("num1 | num2 = ");PrintBase2(num1 | num2);putchar('\n');
        printf("num1 ^ num2 = ");PrintBase2(num1 ^ num2);putchar('\n');
        return 0;
}
int Trans2Int(char * s){
        int i, length = strlen(s), result=0;
//每读取一位所有位左移,确保权值
        for(i=0; i<length; i++){
                result <<= 1;
                result += s[i] - '0';
        }
        return result;
}
void PrintBase2(int n){
//设置整数最后一位是1
        int mask=1;
        bool start_print = false;
//我的系统中整数时32位,使整数首位为1
        mask <<= 31;
//如果n的第一位是1,则说明是负数,打印符号
        if(n & mask)
                putchar('-');
        while(mask == 0){
//掩码右移,逐个确认每个为的真假,掩码为1的位超出整数的边界时停止循环
                mask >>= 1;
//抛去前面为0的位
                if(!(n & mask) && !start_print)
                        continue;
//设置开始打印条件
                start_print = true;
                if((n & mask))
                        putchar('1');
                else
                        putchar('0');
        }
        return ;
}

按照逻辑,这个程序可以打印出正确的结果,但是最后运行下来却时不断打印1,即mask为1的位始终没有超出边界

C语言移位运算符的规则引起的bug_#include_02

 

 

 C语言移位运算符的规则引起的bug_移位运算符_03

 

 

 我已经了解过移位运算符的填充规则,多出的位会用0来填充,但这样说不通,于是我写了个简单的程序去验证移位运算符的规则,结果就发现了移位运算符一个神奇的隐藏规则

写的第一个程序

#include<stdio.h>

int main(void)
{
        int i=1;
        while(i){
                i <<= 1;
                printf("%d ", i);
        }
        return 0;
}

左移的结果没有任何问题,果然是使用0来填充

[lyb@localhost not_myc]$ vi try.c
[lyb@localhost not_myc]$ gcc try.c -o test
[lyb@localhost not_myc]$ ./test
2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912 1073741824 -2147483648 0 [lyb@localhost not_myc]$ 

接着又写了右移,结果却是开始魔幻了起来,完全出乎意料

#include<stdio.h>

int main(void)
{
        int i=1;
        i <<= 31;
        while(i){
                i >>= 1;
                printf("%d ", i);
        }
        return 0;
}

程序不断打印-1

C语言移位运算符的规则引起的bug_c语言_04

 

在c语言中采用补码方案表示负数,所以-1在c语言中就是第一位是1其它位都是0,即10000000000000000000000000000000。也就是说第一次操作将1第一位变成1之后,所有的右移操作都没起作用。

至此我才找到了bug的原因,即移位运算符进行右移的时候无法改变符号位,左移却可以。根据这条规则,改进了之前的代码

#include<stdio.h>
#include<stdbool.h>
#include<string.h>
int Trans2Int(char *);
void PrintBase2(int);
int main(int argc, char * argv[]){
        int num1, num2;
        num1 = Trans2Int(argv[1]);
        num2 = Trans2Int(argv[2]);
        printf("num1 = ");PrintBase2(num1);
        printf(" num2 = ");PrintBase2(num2);putchar('\n');
        printf("~num1 = ");PrintBase2(~num1);
        printf(" ~num2 = ");PrintBase2(~num2);putchar('\n');
        printf("num1 & num2 = ");PrintBase2(num1 & num2);putchar('\n');
        printf("num1 | num2 = ");PrintBase2(num1 | num2);putchar('\n');
        printf("num1 ^ num2 = ");PrintBase2(num1 ^ num2);putchar('\n');
        return 0;
}
int Trans2Int(char * s){
        int i, length = strlen(s), result=0;
        for(i=0; i<length; i++){
                result <<= 1;
                result += s[i] - '0';
        }
        return result;
}
void PrintBase2(int n){
        int mask=1;
        bool start_print = false;
        mask <<= 30;
        if(n & (mask << 1))
                putchar('-');
        if(!n){
                putchar('0');
                return ;
        }
        while(mask){
                if(!(n & mask) && !start_print){
                        mask >>= 1;
                        continue;
                }
                start_print = true;
                if((n & mask))
                        putchar('1');
                else
                        putchar('0');
                mask >>= 1;
        }
        return ;
}  

完美运行

[lyb@localhost not_myc]$ vi 15_2.c
[lyb@localhost not_myc]$ gcc 15_2.c -o test
[lyb@localhost not_myc]$ ./test 100001 11000010
num1 = 100001 num2 = 11000010
~num1 = -1111111111111111111111111011110 ~num2 = -1111111111111111111111100111101
num1 & num2 = 0
num1 | num2 = 11100011
num1 ^ num2 = 11100011
[lyb@localhost not_myc]$ 

总结:进行位右移操作的时候,符号位不会发生改变,进行左移的时候,符号位可以改变。