位运算符概览
符号 | 描述 | 运算规则 |
---|---|---|
& |
与 | 两个位都为1时,结果才为1 |
| |
或 | 两个位都为0时,结果才为0 |
^ |
异或 | 两个位相同为0,相异为1 |
~ |
取反 | 0变1,1变0 |
<< |
左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> |
右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
定义与基本用途
按位与运算符&
参与运算的两个对象同时为1,结果才为1,否则结果为0。
注意:负数按补码形式参加按位与运算。
示例:
1 & 1 = 1
1 & 0 = 0
0 & 0 = 0
0 & 1 = 0
与运算用途:
- 清零
如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。 - 取一个数的指定位
比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。 - 判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。
按位或运算符|
参加运算的两个对象只要有一个为1,其值为1。
注意:负数按补码形式参加按位或运算。
示例:
1 | 1 = 1
1 | 0 = 1
0 | 0 = 0
0 | 1 = 1
或运算用途:
- 常用来对一个数据的某些位设置为1
比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。
异或运算符^
参加运算的两个对象,如果两个相应位相同为0,相异为1。
示例:
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 0 = 0
0 ^ 1 = 1
规律:
- 1 与任何数异或相当于对这个数取反
- 0 与任何数异或还得原数
性质:
- 交换律
- 结合律
- 对于任何数x,都有 x^x=0,x^0=x
- 自反性: a^b^b=a^0=a
异或运算用途:
- 翻转指定位
比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。 - 交换两个数
void Swap(int &a, int &b){ if (a != b){ a ^= b; b ^= a; a ^= b; } }
取反运算符~
对一个二进制数按位取反,即将0变1,1变0。
示例:
~1 = 0
~0 = 1
取反运算用途:
- 使一个数的最低位为零
使a的最低位为0,可以表示为:a &1。1的值为 1111 1111 1111 1110,再按”与”运算,最低位一定为0。因为” ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
左移运算符<<
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
示例:设 a=1010 1110,a = a << 2 将a的二进制位左移2位、右补0,即得a=1011 1000。
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2,1 << n
= 2 ** n
。
右移运算符>>
将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
示例:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。
操作数每右移一位,相当于该数除以2。
复合赋值运算符
&=
例:a&=b 相当于 a=a&b
|=
例:a|=b 相当于 a=a|b
>>=
例:a>>=b 相当于 a=a>>b
<<=
例:a<<=b 相当于 a=a<<b
^=
例:a^=b 相当于 a=a^b
综合运用示例
异或加密
异或加密是一种常见的对称加密算法。异或运算的自反性是异或加密的理论基础,自反性: a^b^b = a^0 = a
。即数a对数b进行连续两次异或还会得到数a。那么双方使用相同的秘钥b,就可以对任何数进行加密和解密了。
实际运用时,一般以字节为单位进行异或处理,因此明文内容中的字节与加密串中需要运算的字节需要确立一个对应关系,即参与确定运算的两个对象。这里使用按序取余的做法选择异或运算对象字节。
下面是Golang实现的异或加密示例代码:
func XorEncrypt(data, encryptString string) []byte {
var val []byte
// 对于字符串,len()获取的是字节数量
dataLen, strLen := len(data), len(encryptString)
dataSlice, encryptStringSlice := []byte(data), []byte(encryptString)
for i := 0; i < dataLen; i++ {
val = append(val, dataSlice[i]^encryptStringSlice[i%strLen])
}
return val
}
PHP实现:
function XorEncrypt($data, $encryptString){
// strlen() 获取字节数量,mb_strlen()获取字符数量
$dataLen = strlen($data);
$strLen = strlen($encryptString);
$ret = "";
for($i = 0; $i < $dataLen; $i++){
$ret .= $data{$i} ^ $encryptString{$i % $strLen};
}
return $ret;
}
由于是按字节异或,加密后的密文一般是乱码,无法正常显示,常见处理方式是再对密文进行base64编码处理以便能正常显示。
加密示例:
// 原文
var data string = "name=张三&age=18"
// 加密串,随机生成即可
var enc string = "ASL#K12%$24$%AS9))21"
// 加密后并base64,结果为:LzIhRnbUjoXAir0CRCY2BBgR
var encStr string = base64.StdEncoding.EncodeToString(XorEncrypt(data, enc))
解密示例:
// 密文
$encStr = "LzIhRnbUjoXAir0CRCY2BBgR";
// 相同的加密串
$enc = "ASL#K12%$24$%AS9))21";
// base64解码后解密,结果为:name=张三&age=18
$data = XorEncrypt(base64_decode($encStr), $enc);
按位枚举
按位枚举,就是将一个数转换为二进制,二进制的每一位为一个枚举位,一个比较经典的案例就是PHP的错误级别枚举。
值 | 移位表示 | 常量 | 说明 |
---|---|---|---|
1 | 1<<0 | E_ERROR | 致命的运行时错误,一般是不可恢复的情况,例如内存分配导致的问题,后果是导致脚本终止、不再继续运行。 |
2 | 1<<1 | E_WARNING | 运行时警告(非致命错误),仅给出提示信息,但是脚本不会终止运行。 |
4 | 1<<2 | E_PARSE | 编译时语法解析错误,仅由分析器产生。 |
8 | 1<<3 | E_NOTICE | 运行时通知,表示脚本遇到可能会表现为错误的情况,但是在可以正常运行的脚本里面也可能会有类似的通知。 |
16 | 1<<4 | E_CORE_ERROR | 在 PHP 初始化启动过程中发生的致命错误,类似 E_ERROR,但是是由 PHP 引擎核心产生的。 |
32 | 1<<5 | E_CORE_WARNING | PHP 初始化启动过程中发生的警告(非致命错误),类似 E_WARNING ,但是是由 PHP 引擎核心产生的。 |
64 | 1<<6 | E_COMPILE_ERROR | 致命编译时错误,类似 E_ERROR,但是是由 Zend 脚本引擎产生的。 |
128 | 1<<7 | E_COMPILE_WARNING | 编译时警告(非致命错误),类似 E_WARNING,但是是由 Zend 脚本引擎产生的。 |
256 | 1<<8 | E_USER_ERROR | 用户产生的错误信息,类似 E_ERROR,但是是由用户自己在代码中使用 PHP 函数 trigger_error() 来产生的。 |
512 | 1<<9 | E_USER_WARNING | 用户产生的警告信息,类似 E_WARNING,但是是由用户自己在代码中使用 PHP 函数 trigger_error() 来产生的。 |
1024 | 1<<10 | E_USER_NOTICE | 用户产生的通知信息,类似 E_NOTICE,但是是由用户自己在代码中使用 PHP 函数 trigger_error() 来产生的。 |
2048 | 1<<11 | E_STRICT | 启用 PHP 对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。 |
4096 | 1<<12 | E_RECOVERABLE_ERROR | 可被捕捉的致命错误,表示发生了一个可能非常危险的错误,但是还没有导致 PHP 引擎处于不稳定的状态。如果该错误没有被用户自定义句柄捕获,将成为一个 E_ERROR,从而使脚本终止运行。 |
8192 | 1<<13 | E_DEPRECATED | 运行时通知,启用后将会对在未来版本中可能无法正常工作的代码给出警告。 |
16384 | 1<<14 | E_USER_DEPRECATED | 用户产生的警告信息,类似 E_DEPRECATED,但是是由用户自己在代码中使用 PHP 函数 trigger_error() 来产生的。 |
32767 | ~(-1<<15) | E_ALL | 所有错误和警告信息。 |
运算技巧
- 利用按位或组合错误级别
E_NOTICE | E_WARNING | E_ERROR
- 利用按位与和取反剔除不需要的级别,即将指定bit设置为0
E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE