关于操作符/位操作 的操作方法和应用 附加(整形提升)(C语言)

关于操作符/位操作 的操作方法和应用 附加(整形提升)(C语言)

1.了解什么是位操作(二进制数,位)
2.关于位操作符的详解( ‘&’ ‘|’ ‘~’ )
3.位移操作符(">>","<<")
4.位异或的一些妙用

Hellolo
关于这一章的话,之所以想成写一篇博客是因为这一章在我们学校高级语言程序课中基本就是草草概括,正巧寒假开始数据结构,也在Leetcode上了解到可以巧妙运用位操作来解决一些时间空间”双杀“的问题。
所以,一鼓作气,写下这篇Blog,也好让自己巩固这一方面知识。

1.位操作
相信大家对于操作符都不陌生,每个语言都有着自己的操作符,在C语言中,操作符分为:
算术、位移、位、赋值、单目、三目(条件)、关系、逻辑、逗号 操作符
而本篇blog主要介绍位操作符:
要想知道位操作符,首先得知道什么是”位“
了解“位”之前,我觉得很有必要了解一下“二进制位计算法”
进制
很多时候,身边没有计算机的情况下,如果我们要转换进制数,其实是一件非常简单的一件事。举个栗子,我们生活中最常使用的十进制数字:1729 来说
我们可以这样转化:每个数字都有着各自的权重 ,9是个位,2是十位,7是百位,1是千位,所以 1729 = 11000 + 7100 + 210 + 91
当然这种方法我个人不喜欢,不如换一个方法,这个方法专业叫法是”以N为基底书写XXX“(这里的N为进制数,XXX为你要转化的数字)
就拿刚刚的例子,就为,”以10为基底书写1729“
那么好了,这样就是:1729 = 1 * 10^3 + 1 * 10^2 + 1 * 10^1 + 1*10^0

这是我们的10进制
但是机器可就没我们这么厉害了,它只识得1和0 (关闭或者打开)
这也就导致我们经常在编程过程中运用最多的是二进制

但是了解了刚刚的”基底书写“,二进制的转化也很简单不是吗?
eg:将二进制101转化成十进制?
那么就是 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5

现在我们都知道了,机器相对于聪明的人类来讲,思考和计算还是比较吃力的。但身为一个程序员,我们必须要掌握如何利用二进制系统来表示1字节和整数。

比如,在现实生活中我出去买零食,10元的奶茶+5元的薯片 让我刚刚踏入幼儿园的表弟来算都知道 10 + 5 = 15 需要15元
而计算机则不然,它是这样算的*(这里为了好理解,用C语言来解释)*:

int a = 10;
int b = 5;
int c = a + b;
//最终需要c,这么多的钱 

当然了,如果真是眼看着这样的话,那我刚刚说的都白说了
实际上:

int a = 10;
//10 -转化为二进制
//00000000000000000000000000001010 - 10
int b = 5;
//5 - 转化为二进制
//00000000000000000000000000000101 - 5
int c = a + b;
//00000000000000000000000000001010
//00000000000000000000000000000101
//上述两式子相加后得到
//00000000000000000000000000001111 - 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 = 15

这就是计算机运用的二进制计算的原理

这就引出了以下”三匹马”(个人觉得有意思就这么想出了这种记法~)
原码、补码、反码
上述代码中
将10转化为二进制后得到的就是该数字的*“原码”*
即00000000000000000000000000001010 - 10的原码
而存于计算机内存中的却不是原码
而是补码,补码很重要!因为存在计算机内存中,所以计算和处理的时候,都是补码在工作!
补码怎么得到呢?(这里拿int整数来举例子)
这里要看这个数字的正负了(这里由于太深奥,总的来说,有符号的整数取决于硬件,和各种语言没任何关系。具体嘛。。我也不知道~~~QAQ)
这里引出一个叫做 符号量(sign - magnitude) 的表示法 即正数符号位为0,负数符号位为1。
在这基础上:
1.正整数 —> 原码反码补码统统相同
2.负整数 —> 原码反码补码需要通过计算得到 (原码–>反码–>补码)

这里就不继续解释这“三匹马”了,停!
让我们赶紧回到刚刚的代码中,现在我们晓得了,正整数的原反补都是一样的,那么就好办了,把补码两者相加,得到的即就是c的补码–>原码
00000000000000000000000000001010 ----10的补码
00000000000000000000000000000101 ---- 5的补码

=
00000000000000000000000000001111 — 15的补码 也即原码

这样大家就都明白了吧~~
但是这个时候就有人问了,我需要知道这玩意干啥!?
emmm说实话一开始我也觉得知道这玩意好像没啥用,但是你看看下面的代码,看看你能不能一下子算出结果~

char a = 3;
char b = 127;
char c = a + b;
printf("%d ",c);
//C为多少?

哦!对!还要再讲一个东西!不然这题解不了。
叫做————整型提升

表达式的整形原型要在CPU的相应运算期间内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器长度。
因此,即使两个char类型相加,在CPU执行时,也要先转换成int类型才能计算

好了,我说完了。我也把答案放出来吧
在这里插入图片描述
不能理解很正常,我把过程再放出来你就理解了~
在这里插入图片描述
这里有个要注意的地方,整形提升是将符号位补齐哦!

好了,终于我们终于了解二进制运算了!
那我们可以用二进制计数法来了解位的操作

##位操作

首先先声明一下,本Blog用的是8位二进制数,也即一个字节(Byte)
除了第一位我们刚刚介绍的
符号位*(sign - magnitude) 外,其他0~7位,我们称为“位” 从左到右,编号为 7 ~ 0 其中7为最高位,0为最低位。*

还有要注意的是,运算过程中,统统为补码在工作哦,前面提到过了。

  1. 按位与操作符‘ &’
    不光是C语言,“与”我们已经很熟悉了,表达式中,只要有一方为假(0),即整个式子就为假(0),只有当两方为真(1),最后返回值才为真(1)
    举个例子:
    你和你女朋友出去约会:你问她想去哪?
    她说:“海边和图书馆”
    懂我意思吗?那肯定两个地方都要去!

eg: (00010010) & (10011110) = 00010010
(10010011) & (00111101) = 00010001

还可以顺带掌握 “&=”
a &= 0001 等价于 a = a & 0001

  1. 按位或 ’ |’
    表达式中,只要一方为真(1),即为真(1)

    和上面的例子一样
    但是这次她说:海边或图书馆吧
    那你选择一个你觉得更浪漫的地方去就好了~

eg:(00010010) | (10011110) = 10011110
(10010011) | (00111101) = 10111111
相同的,也有 “|=”
用法和上面"&="一样

  1. 按位异或 ’ ^’
    表达式中,相异为真(1),相同为假(0)
    这个。。不太好举实体例子,但是!聪明的你看完下面的代码肯定也很好理解的!
    eg:(10010011) ^ (00111101) = 10101110

    还有一个 “^=”,用法见上。

4.按位取反 ’ ~‘
把补码中1变成0,0变成1就好了
eg:~(10011010) = (01100101)

##位移操作符

*同样的,这里也使用二进制数

1.左移 " << "
规则:整体向左移动N位(抛弃),然后右边补0
假设 a = 1
现在 int b = a<<1
未移动前:
在这里插入图片描述
移动后:
在这里插入图片描述
eg : int b = 5 << 1
b = 10
所以,左移其实有乘以2^n的效果

2 右移 “>>”

规则:1.算术右移( 左边用原来的数字补齐,右边丢弃)
2.逻辑右移 (左边用0补齐,右边丢弃)

这里要说明一下,现在大部分编译器支持的都是算术右移,所以我们这里只讲算术右移,逻辑右移如果有兴趣可以下去自己试试,两者有时结果不一样

未移动前:
在这里插入图片描述
移动后:

在这里插入图片描述

eg: int b = 8 >> 1 = 4
所以和左移相反,右移有着除以2^n的效果

##按位异或的一些妙用

呼,终于写到了这。
其实到了这才真正开始烧脑

首先,按位与是一个很有意思的运算方式。
首先先介绍一下按位异或的两个小特点:
1.0与N异或 = N
即 0 ^ 2 = 2; 0 ^ 4 = 4;
2.N与N异或 = 0
即 1 ^ 1 =0; 2 ^ 2 = 0;
知道了这两点之后,再来看接下来的内容

在这里插入图片描述
这是一道Leetcode上的题目
博主开始遇到这题第一想法是排序后与0~n的的数组进行比较,然后找出漏掉的那个数
但是!这里有限制,那就没法排序了啊,要知道博主若使用快速排序(时间复杂度为(O(n*log(n))都不行,更何况冒泡排序(时间复杂度为O(N^2))
所以,学到了一招骚方法

用异或来写
思路:设置一个num = 0;
用num 去和 缺少数的数组A进行异或,再和数组B [0,N]去异或
答案就是最后异或出来的数字
原理:相同的两个数字异或为0,而缺少的数字n在数组B中存在,所以最后得出结果。

这样虽说还是遍历了一遍,但是满足了题目要求~

class Solution {
    
    
public:
    int missingNumber(vector<int>& nums) {
    
    
        int n=nums.size();
        int ans=0;
        for(int i=1;i<n;i++){
    
    
            ans^=i^nums[i];
        }
        return ans^nums[0]^n;
    }
};

当然还有很多类似的题目,我觉得凡是需要在数组中找寻某个数字,和别的数组比较的那种
都可以用异或这一特点来破解。

The end
2021/1/29 *文章中摘取了《C premier Plus》
leetcode 作者 snowmonster的代码.

猜你喜欢

转载自blog.csdn.net/m0_50986342/article/details/113399611