操作符详解分为上下两篇本篇为上篇主要从二进制角度介绍了一系列操作二进制的操作符同时还包含了一下计算机组成原理的知识。八、操作符详解一、操作符和进制进制二进制转十进制从右向左数二进制的第几位表示2的第几次方对应位数的位次权重乘以对应位置的数值加起来就是十进制十进制转化为二进制短除法将余数逆序就是对应的二进制二进制转换为八进制八进制的最高位是7刚好就是111二进制所以将二进制从低位到高位每一位转换为对应的10进制数就是一个八进制数值如下图同理1111表示16进制的15十进制数所以每4位二进制数值转换为十进制然后排序就是16进制注意在c语言中数值前面加上0零就表示这个数是八进制加上x就表示16进制原码、反码、补码整数的二进制有三种表示方式分别是原码反码补码也就是说这是关于整数的性质整数分为signed有符号整型和unsigned无符号整型。其中正整数的二进制的三种码都一样无符号整型的三种码都一样而负整数的三种二进制表示方法不一样整型的保留字是int它的字节是4每个字节有8位所以一共是32个位也就是格子。有符号整型的第32个位是符号位用0表示正用1表示负如图所示对于负整数的二进制原码直接将负整数转化为二进制并补齐空位这就是原码反码符号位不变的情况下将数值位全部取反也就是1变成0,0变成1得到反码补码补码就是在反码的基础上加一例如三种表示方法的转换补码到原码也可以使用取反加1的操作整数数据在内存中都是用补码来存储的原因如下使用补码可以无视符号的影响直接计算加法也就是说补码实现了数值域和符号域的统一使用补码可以将加减法统一在CPU中只有加法实现减法就是将加法变换为减法例如1-11-1原码与补码的相互转换可以用同一种放方法这样节省了一个电路元件我们用补码实现1-1如图所示int main() { //用补码实现1-1 // 也就是实现1-1 //00000000000000000000000000000001-这是1的原码也是补码 //10000000000000000000000000000001-这是-1的原码 //如果直接相加得到 //10000000000000000000000000000010-这是-2 // //11111111111111111111111111111110-这是-1的反码 //11111111111111111111111111111111-这是-1的补码 //00000000000000000000000000000001-这是1的原码也是补码 //使用两个补码相加得到 //100000000000000000000000000000000-舍去最高位后 //就是00000000000000000000000000000000-这就是1-1的结果 }内存里面用补码计算得到的还是补码但是将补码显示给你看的时候是原码二、移位操作符与移位操作符只能对整数的二进制的补码进行操作左移右移的位数不能超过数值本身的位数对于一个字节32位移动的位数不能超过32左移操作符规则左边丢弃右边补零。效果左移一位数值翻倍左移x位数值变为原来的2^x倍前提是左移不会导致左边的1消失也就是说数值比较小的时候这个效果成立。如下代码右移操作符规则有两种逻辑右移右边丢弃左边补零算术右移右边丢弃左边补齐原来的符号的数值也就是符号不变这两个规则由编译器决定大部分都是算术右移在vs中是算术右移可以用负数验证我们有-6验证一下代码如下对于负数在可控范围内右移一位的值是原数值的一半如果是奇数那就减一再除以二例如-9右移后得到-5三、位操作符、|、^、~注意位操作符只能操作二进制的位且只能是整数的二进制识别和区分按位与有0是0两个都是1才是1| 按位或有1就是1两个都是0才是0^按位异或相同就是0不相同就是1~按位取反每一位都取反了1.按位与 两个整数的二进制数对他们的对应位操作对应位上的数值有0则是0两个都是1则是1。例如下代码2.按位或 ||同样是对两个整数的二进制的对应位数操作有1则是1同时为0则是0如下图3.按位异或 ^^按位异或他表示相同就是0相异就是1代码如下4.按位取反 ~~按位取反它表示对一个二进制数操作每一位取反效果表现为数值取相反数然后减一代码如下5.应用例题1应用题一位操作符的简单应用——不创建第三个变量实现两个整数的交换方法一运用加减法运算代码如下但是有个弊端当a或者b足够大的时候ab超过了int的32位就会导致数据丢失最终计算结果不正确//实现两个整数的交换#includestdio.h int main() { int a 0; int b 0; scanf(%d %d, a, b); printf(a%d,b%d\n, a, b);//交换前 a a b; b a - b;//(ab)-b a a - b;//(ab)-a printf(a%d,b%d\n, a, b);//交换后 return 0; }方法二运用按位异或^代码如下按位异或的运算规则中有相同为0相异为1a ^ a 0 a ^ b ^ a b也就是按位异或是满足交换律的#includestdio.h int main() { int a 0; int b 0; scanf(%d %d, a, b); printf(a%d,b%d\n, a, b);//交换前 ​ a a ^ b; b a ^ b; a a ^ b; ​ printf(a%d,b%d\n, a, b);//交换后 return 0; ​ }可以将交换过程作为如下理解已知a ^ b这个密码当我再知道b就可以解除b得到a当我再知道a就可以解除a得到b而按位异或是不会进位的所以运算过程中不会有数据溢出弊端使用按位异或并不是万事大吉了这个方法会导致代码可读性下降还有就是效率低下最重要的是只能操作整数对于小数无法解决常用我们还是使用第三变量来临时存储然后转换2应用题二位操作符的简单应用——编写代码求内存中存储的整数的二进制中的1的个数方法一对于一个整数它的二进制是32位以15为例子00000000000000000000000000001111求它的1 的个数我们只需要将每一位拿出来和 1 比较如果是 1 就加一同理与求十进制1233543中的3的个数的方法也就是将1233543%10就可以得到最后一位然后1233543/10得到123354再123354%10又可以得到最后一位直到所有位比较完就OK了二进制中应该模2代码如下//求内存中整数的二进制中的1的个数 #includestdio.h int count_bit1_of_n(int n) { int count 0; while (n) { if (1 n % 2) count; n / 2; } return count; } int main() { int n 0; scanf(%d, n); //使用函数 int ret count_bit1_of_n(n); printf(%d\n, ret); return 0; }但是这样是有弊端的当我需要计算负数的时候就会出错例如-1的补码是11111111111111111111111111111111它有32个1但是代码运算的是0方法二使用移位和按位与以15为例子这是15的原码也是补码00000000000000000000000000001111我们每次对它右移右移时舍去右端左端添加符号相同的元素将移位后的数与00000000000000000000000000000001使用按位与有0则是0同时为1则是1而00000000000000000000000000000001的前31位都是0最后一位是1所以如果按位与1那么只可能得到0或者1。而得到0还是1就是由00000000000000000000000000001111的最后的这一位数值决定综上就是每次移位然后按位与1然后与1比较如果是1就count就加加。代码如下int count_bit1_of_n(int n) { int count 0; int i 0; for(i0;i32;i) { if (1 ((n i) 1)) count; } return count; } int main() { int n 0; scanf(%d, n); //使用函数 int ret count_bit1_of_n(n); printf(%d\n, ret); return 0; }优化我们发现上述代码一定需要循环32次效率太低了我们有更高的算法使用n(n-1)解释如下图可以发现每执行一次这句语句n的二进制数值的 1 就减少一个当有多个 1 的时候是从右向左减少所以这个语句可以执行的次数就是数值n的二进制的 1 的数量所以代码如下//最高效的判断数字n的二进制中有多少个1 int count_bit1_of_n(int n) { int count 0; while (n) { n n (n - 1); count; } return count; } int main() { int n 0; scanf(%d, n); //使用函数 int ret count_bit1_of_n(n); printf(%d\n, ret); return 0; }迁移应用判断一个数是不是2的整数次方我们发现如果这个数是2的整数次方那么它的二进制数值上面只有一个1使用n n(n-1)这个语句只要得到的 n 为 0 这样就可以判断了代码如下//迁移应用判断数字n是不是2的整数次方(index) #includestdio.h #includestdbool.h ​ bool is_two_index(int n) { if ((n (n - 1)) 0) return true; else return false; } int main() { int n 0; scanf(%d, n); //使用函数 bool ret is_two_index(n); printf(%d\n, ret);// 1表示是0表示假 return 0; }3应用题三练习3将数字n的二进位的第5位给改为一再改回来使用按位与按位或移位等操作符思路如下那么如何变回来呢我们只要将第5位变成0就OK了使用11101111按位与上a就行有0是0同时为1才是1并且这个11101111的数字刚好就是 1 左移4位后再按位取反的数字代码如下#includestdio.h int main() { int a 13; //00001101-这是13的二进制的补码也是原码 //将它的第五位变为1只需要用00010000按位或就OK了有一就是一同时为零才是零 // 00001101 //|00010000——————这个数字由1向左移动5-1位 // 00011101,也就是161329 aa | (1 4); printf(%d\n, a); a a (~(1 4)); printf(%d\n, a); return 0; }