位运算基本知识浅谈

简介

我们知道,计算机中所有数都是二进制存储的

位运算就是对正数的二进制位进行的运算

运算说明

首先明确一点,位运算是对正数进行的运算,所以下面的各语句中,变量类型均默认定义为int

按位取反not

表达式~a,按二进制位运算,将每一位取反

简单来说,即“0变1,1变0”

左移shl

表达式a << n,其中n为正整数,表示在二进制下,将a左移n位(在a的二进制表示后面添上n个0)

实际上等于$a*2^n$

右移shr

表达式a >> n,其中n为正整数,表示在二进制下,将a右移n位(将a的二进制表示后面n位舍弃)

实际上等于$\lfloor{\frac{a}{2^n}}\rfloor$

按位与and

表达式a & b,按二进制位运算,位数较小的补齐前导0,对于每一位,二进制表示均为1,则结果为1,否则为0

简单来说,即“有0得0,全1得1”

按位或or

表达式a | b,按二进制位运算,位数较小的补齐前导0,对于每一位,二进制表示均为0,则结果为0,否则为1

简单来说,即“有1得1,全0得0”

按位异或xor

表达式a ^ b,按二进制位运算,位数较小的补齐前导0,对于每一位,二进制表示两变量相异为1,相同为0

简单来说,即“相同得0,相异得1”

异或的逆运算

异或的逆运算是它本身,也就是说,一个值两次异或同一个值,最后得到它本身,即a == a ^ b ^ b

异或的简单运用

“异或的逆运算是它本身”,我们可以发散思维,得出异或的一些简单运用

简单加密

如果你得到一个整数作为明文,想要加密,最简单的方法就是选取另一个整数作为密钥,对明文和密钥进行异或运算,结果即为得到的暗文,而接收暗文的人只需手持一样的密钥,将暗文与密钥进行异或运算,即可重新得到明文

(似乎没有什么用的) 不需要中间变量的交换过程

现在,有两个变量,ab,需要将两个变量的值交换,又不想定义中间变量,那可以考虑用异或运算进行交换,代码是这样的:

1
2
3
a ^= b;
b = a ^ b;
a ^= b;

现在来考虑这段代码执行交换的合理性,之前提到过,a == a ^ b ^ b,那么第一行中a ^= ba的值变为a ^ b,再到第二行执行时,相当于b的值被重新赋值为a ^ b ^ b,也就是a变量的初值,最后到第三行执行时,a的值则被赋值为a ^ b ^ a,也就成了b变量的初值,完整实现了交换功能

小拓展:

上述交换功能的本质是对于互逆运算的如下利用:

定义符号$与#,使之互为逆运算

对于如下代码块

1
2
3
a <- a # b
b <- a $ b
a <- a $ b

执行第一句后a的值变为a # b,第二句执行时b的值被赋为a # b $ b,由于$与#互逆,则a # b $ b = a,所以b的值被赋为a变量的初值,同理,第三行使a被赋值为b变量的初值

正因为异或运算的逆运算是其本身,才会有看起来这样离谱的交换代码

同样是这个本质,加减互为逆运算,用起来就不会像异或这样看起来十分离谱

1
2
3
a += b;
b = a - b;
a -= b;

运算优先级

所有运算后赋值操作<或<异或<与<左移=右移<其他所有除括号以外的运算<取反<括号

括号yyds

总结

本文简单陈述了C语言基础知识位运算,并对异或运算进行了小小的拓展,尽可能浅显地使阅读本文的诸君理解位运算,望共勉之

P.S. 笔者如有陈述不周之处还望斧正