6.1 位运算符与溢出运算符

很多编程语言都支持位运算符,位运算符的主要功能是对二进制数据进行位运算等操作。而溢出运算符是Swift语言独有的,由于Swift语言对代码安全性的注重,正常的运算是不允许出现溢出行为的。开发者使用溢出运算符可以控制是否允许溢出运算。

6.1.1 位运算符的应用

在计算机中,数据都是以二进制的形式存储的,位运算也是专门针对二进制数据的一种运算方式。在Swift语言中,开发者在创建数值变量时可以通过追加“0b”前缀的方式将数值设置为二进制。使用Xcode开发工具创建一个命名为AdvancedOperators的playground文件,在其中进行代码演示。创建一个UInt8类型的变量,将十进制数8以二进制的方式赋值,示例如下:

    //十进制数8
    var a:UInt8 = 0b1000

前面介绍过UInt8类型,其为8位的无符号整型,也就是说,任何一个UInt8类型的变量都是采用8个二进制位来存储数据的。因此读者可以理解为a变量实际存储的数据为00001000。位运算的实质就是对数据的每一个二进制位进行逻辑运算。

Swift语言支持C/Objective-C语言中的全部位运算符,其中包括按位取反运算、按位与运算、按位或运算、按位异或运算、按位左移运算以及按位右移运算。

按位取反运算符“~”的作用是将数据的每一位都进行取反操作,即如果当前位为0,则运算后变为1,如果当前位为1,则运算后变为0。对上面创建的变量a进行按位取反运算后,其存储的数据将变为11110111,即十进制数247,示例如下:

    //运算后 a 的值变为十进制数247
    a = ~a

按位与运算符“&”需要有两个操作数,其作用是将两个操作数相同的位进行逻辑与运算。即如果两个对应位的值都为1,则运算后此位结果为1;如果其中有一个位的值为0,则运算后此位结果为0。示例如下:

    //使用二进制数11110000与a进行按位与运算,运算结果为11110000,即十进制数240
    a = 0b11110000&a

按位或运算符“|”需要有两个操作数,其作用是将两个操作数相同的位进行逻辑或运算。即如果两个对应位的值有一个为1,则运算后此位结果为1;如果两个对应位的值都为0,则运算后此位结果为0。示例如下:

    //使用二进制数11111111与a进行按位或运算,结果为11111111,即十进制数255
    a = 0b11111111|a

按位异或运算符“^”需要有两个操作数,其作用是将两个操作数相同的位进行逻辑异或运算。即如果两个对应位的值相同,则运算后此位的值为0;如果两个对应位的值不同,则运算后结果为1。示例如下:

    //使用二进制数11110000与a进行按位异或运算,结果为00001111,即十进制数15
    a = 0b11110000^a

按位左移运算符“<<”用于将数据每一位上的值进行左移操作,示例如下:

    //将a按位左移1位,结果为00011110,即十进制数30
    a = a<<1

与按位左移运算对应,按位右移运算符“>>”用于将数据每一位上的值进行右移操作,示例如下:

    //将a按位右移1位,结果为00001111,即十进制数15
    a = a>>1

提示

进行按位左移或者右移运算的时候,有可能出现丢失数据位的情况。例如,对于UInt8类型,将二进制数据00001111向左移动6位结果为11000000,同样,将二进制数据11110000向右移动6位结果为00000011。

6.1.2 溢出运算符

Swift语言十分注重安全性,在编写代码时,如果出现了数据溢出,就会直接出现运行时错误。而溢出运算符这种设计将代码的不确定性降低了很多。所谓溢出,是指超出数据所属类型的最大值或者最小值。以UInt8来说,其最大值为二进制数11111111,即十进制数255。如果将UInt8类型的变量设置为255后再让其加1,就会出现数据溢出,示例如下:

    var b:UInt8 = 255
    //代码运行到这里会出现错误,运行直接中断
    b = b+1

如果开发者确实需要溢出操作而不是无意留下的小错误,Swift语言中也提供了支持溢出操作的溢出操作符,示例如下:

    var b:UInt8 = 255
    //进行支持溢出的加操作,b的值将变为0
    b = b &+ 1
    //进行支持溢出的减操作,b的值将变为255
    b = b &- 1
    //进行支持溢出的乘操作,b的值将变为254
    b = b &* 2

提示

对二进制数据进行乘2运算,实质就是对二进制数据进行左移一位的运算。例如,二进制数据11111111*2=11111110。