位运算符
位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。
Swift支持如下所有C语言的位运算符:
按位取反运算符
按位取反运算符~
对一个操作数的每一位都取反。
这个运算符是前置的,所以请不加任何空格地写着操作数之前。
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
UInt8
是8位无符整型,可以存储0~255之间的任意数。这个例子初始化一个整型为二进制值00001111
(前4位为0
,后4位为1
),它的十进制值为15
。
使用按位取反运算~
对initialBits
操作,然后赋值给invertedBits
这个新常量。这个新常量的值等于所有位都取反的initialBits
,即1
变成0
,0
变成1
,变成了11110000
,十进制值为240
。
按位与运算符
按位与运算符对两个数进行操作,然后返回一个新的数,这个数的每个位都需要两个输入数的同一位都为1时才为1。
以下代码,firstSixBits
和lastSixBits
中间4个位都为1。对它俩进行按位与运算后,就得到了00111100
,即十进制的60
。
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
按位或运算
按位或运算符|
比较两个数,然后返回一个新的数,这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1,或都为1)。
如下代码,someBits
和moreBits
在不同位上有1
。按位或运行的结果是11111110
,即十进制的254
。
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110
按位异或运算符
按位异或运算符^
比较两个数,然后返回一个数,这个数的每个位设为1
的条件是两个输入数的同一位不同,如果相同就设为0
。
以下代码,firstBits
和otherBits
都有一个1
跟另一个数不同的。所以按位异或的结果是把它这些位置为1
,其他都置为0
。
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
按位左移/右移运算符
左移运算符<<
和右移运算符>>
会把一个数的所有比特位按以下定义的规则向左或向右移动指定位数。
按位左移和按位右移的效果相当把一个整数乘于或除于一个因子为2
的整数。向左移动一个整型的比特位相当于把这个数乘于2
,向右移一位就是除于2
。
无符整型的移位操作
对无符整型的移位的效果如下:
已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零0
来填充。这种方法称为逻辑移位。
以下这张把展示了 11111111 << 1
(11111111
向左移1位),和 11111111 >> 1
(11111111
向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的0
是被填充进来的。
let shiftBits: UInt8 = 4 // 即二进制的00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
你可以使用移位操作进行其他数据类型的编码和解码。
let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC, 即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102
let blueComponent = pink & 0x0000FF // blueComponent 是 0x99, 即 153
这个例子使用了一个UInt32
的命名为pink
的常量来存储层叠样式表CSS
中粉色的颜色值,CSS
颜色#CC6699
在Swift用十六进制0xCC6699
来表示。然后使用按位与(&)和按位右移就可以从这个颜色值中解析出红(CC),绿(66),蓝(99)三个部分。
对0xCC6699
和0xFF0000
进行按位与&
操作就可以得到红色部分。0xFF0000
中的0
了遮盖了OxCC6699
的第二和第三个字节,这样6699
被忽略了,只留下0xCC0000
。
然后,按向右移动16位,即 >> 16
。十六进制中每两个字符是8比特位,所以移动16位的结果是把0xCC0000
变成0x0000CC
。这和0xCC
是相等的,都是十进制的204
。
同样的,绿色部分来自于0xCC6699
和0x00FF00
的按位操作得到0x006600
。然后向右移动8們,得到0x66
,即十进制的102
。
最后,蓝色部分对0xCC6699
和0x0000FF
进行按位与运算,得到0x000099
,无需向右移位了,所以结果就是0x99
,即十进制的153
。
有符整型的移位操作
有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。)
有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。0
代表正数,1
代表负数。
其余的比特位(称为数值位)存储其实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看+4
内部的二进制结构。
符号位为0
,代表正数,另外7比特位二进制表示的实际值就刚好是4
。
负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。
我们来看-4
存储的二进制结构。
现在符号位为1
,代表负数,7个数值位要表达的二进制值是124,即128 - 4。
负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。
首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 -1 + -4
的操作,忽略加法过程产生的超过8个比特位表达的任何信息。
第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于2
,右移1位时除于2
。要达到此目的,对有符整型的右移有一个特别的要求:
对有符整型按位右移时,使用符号位(正数为0
,负数为1
)填充空白位。
这就确保了在右移的过程中,有符整型的符号不会发生变化。这称为算术移位。
正因为正数和负数特殊的存储方式,向右移位使它接近于0
。移位过程中保持符号会不变,负数在接近0
的过程中一直是负数。
易百教程移动端:请扫描本页面底部(右侧)二维码并关注微信公众号,回复:"教程" 选择相关教程阅读或直接访问:http://m.yiibai.com 。
加QQ群啦,易百教程官方技术学习群
注意:建议每个人选自己的技术方向加群,同一个QQ最多限加 3 个群。
- Java技术群: 227270512 (人数:2000,免费:否)
- Go开发者群(新): 851549018 (人数:1000,免费)
- PHP开发者群: 460153241 (人数:2000,免费)
- MySQL/SQL群: 418407075 (人数:2000,免费:否)
- 大数据开发群: 655154550 (人数:2000,免费:否)
- Python技术群: 287904175 (人数:2000,免费:否)
- 人工智能深度学习: 456236082 (人数:2000,免费:否)
- 测试工程师群: 415553199 (人数:2000,免费:否)
- 前端开发者群: 410430016 (人数:2000,免费:否)
- C/C++技术群(新): 629264796 (人数:2000,免费)
- Node.js技术群(新): 621549808 (人数:2000,免费)
- PostgreSQL数据库群: 539504187 (人数:1000,免费)
- Linux运维技术群: 479429477 (人数:2000,免费:否)
- Oracle数据库: 175248146 (人数:2000,免费:否)
- C#/ASP.Net开发者: 579821706 (人数:2000,免费)
- 数据分析师群: 397883996 (人数:2000,免费:否)