CPrimerPlus第15章练习题第二题
题目不难,很快程序就写好了
#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的位始终没有超出边界


我已经了解过移位运算符的填充规则,多出的位会用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语言中采用补码方案表示负数,所以-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]$
总结:进行位右移操作的时候,符号位不会发生改变,进行左移的时候,符号位可以改变。
















