本文转载至 http://my.oschina.net/sunqichao/blog?disp=2&catalog=0&sort=time&p=2

除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。

不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出加&+。所有允许溢出的运算符都是以&开始的。

自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。

可定制的运算符并不限于那些预设的运算符,自定义有个性的中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符的实现可以运用预设的运算符,也可以运用之前定制的运算符。

位运算符

位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。

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的过程中一直是负数。

溢出运算符

默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。

例如,Int16整型能承载的整数范围是-32768到32767,如果给它赋上超过这个范围的数,就会报错:

var potentialOverflow = Int16.max // potentialOverflow 等于 32767, 这是 Int16 能承载的最大整数 potentialOverflow += 1 // 噢, 出错了

对过大或过小的数值进行错误处理让你的数值边界条件更灵活。

当然,你有意在溢出时对有效位进行截断,你可采用溢出运算,而非错误处理。Swfit为整型计算提供了5个&符号开头的溢出运算符。

溢出加法 &+

溢出减法 &-

溢出乘法 &*

溢出除法 &/

溢出求余 &%

值的上溢出

下面例子使用了溢出加法&+来解剖的无符整数的上溢出

var willOverflow = UInt8.max // willOverflow 等于UInt8的最大整数 255 willOverflow = willOverflow &+ 1 // 这时候 willOverflow 等于 0

willOverflow用Int8所能承载的最大值255(二进制11111111),然后用&+加1。然后UInt8就无法表达这个新值的二进制了,也就导致了这个新值上溢出了,大家可以看下图。溢出后,新值在UInt8的承载范围内的那部分是00000000,也就是0。

值的下溢出

数值也有可能因为太小而越界。举个例子:

UInt8的最小值是0(二进制为00000000)。使用&-进行溢出减1,就会得到二进制的11111111即十进制的255。

Swift代码是这样的:

var willUnderflow = UInt8.min // willUnderflow 等于UInt8的最小值0 willUnderflow = willUnderflow &- 1 // 此时 willUnderflow 等于 255

有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是-128,即二进制的10000000。用溢出减法减去去1后,变成了01111111,即UInt8所能承载的最大整数127。

来看看Swift代码:

var signedUnderflow = Int8.min // signedUnderflow 等于最小的有符整数 -128 signedUnderflow = signedUnderflow &- 1 // 如今 signedUnderflow 等于 127

除零溢出

一个数除于0 i / 0,或者对0求余数 i % 0,就会产生一个错误。

let x = 1 let y = x / 0

使用它们对应的可溢出的版本的运算符&/和&%进行除0操作时就会得到0值。

let x = 1 let y = x &/ 0 // y 等于 0

Swift 高级运算符的更多相关文章

  1. Swift - 高级运算符介绍

    除了基本运算符之外,Swift还支持位运算和位移运算,包括:   1,按位取反运算:操作符是 ~ 2,按位与运算:操作符是 & 3,按位或运算:操作符是 | 4,按位异或运算:操作符是 ^ 5 ...

  2. 窥探Swift之需要注意的基本运算符和高级运算符

    之前更新了一段时间有关Swift语言的博客,连续更新了有6.7篇的样子.期间间更新了一些iOS开发中SQLite.CollectionViewController以及ReactiveCocoa的一些东 ...

  3. swift学习笔记之-高级运算符

    //高级运算符 import UIKit /*高级运算符(Advanced Operators):位运算符.溢出运算符.优先级和结合性.运算符函数.自定义运算符 位运算符: 1.位运算符可以操作数据结 ...

  4. swift:高级运算符(位运算符、溢出运算符、优先级和结合性、运算符重载函数)

    swift:高级运算符 http://www.cocoachina.com/ios/20140612/8794.html 除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和 ...

  5. swift 笔记 (二十一) —— 高级运算符

    高级运算符 位运算符 按位取反: ~ 按位与运算:  & 按位或运算:  | 按位异或运算:  ^ 按位左移运算: << 按位右移动算: >> 溢出运算符 自从swif ...

  6. Swift高级语法学习总结(转)

    Swift高级语法学习总结 1.函数 1.1 func funcNmae()->(){} 这样就定义了一个函数,它的参数为空,返回值为空,如果有参数和返回值直接写在两个括号里就可以了 1.2 参 ...

  7. Swift高级语法学习总结

    Swift基础语法学习总结Swift高级语法学习总结Swift语法总结补充(一) 1.函数 1.1 func funcNmae()->(){} 这样就定义了一个函数,它的参数为空,返回值为空,如 ...

  8. Swift5 语言指南(二十八) 高级运算符

    除了Basic Operators中描述的运算符之外,Swift还提供了几个执行更复杂值操作的高级运算符.这些包括C和Objective-C中您熟悉的所有按位和位移运算符. 与C中的算术运算符不同,S ...

  9. Welcome-to-Swift-24高级运算符(Advanced Operators)

    除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算. 不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的.溢出行为会 ...

随机推荐

  1. 【Unity笔记】获得鼠标点击屏幕的位置,并转成世界坐标

    Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

  2. 【C#】图像的变形/变换/扭曲。用Emgu或YLScsFreeTransform(FreeImageTransformation)或MagickImage

    需求:将图像变形,如矩形图片变换成梯形的,图素拉伸. 解决方案:目前找到有两种. 使用EmguCV,它是.Net版的OpenCV.推荐直接在VS里的Nuget中搜索EmguCV进行下载. 使用第三方库 ...

  3. 【SVN/Visual Studio】清除/更换AnkhSVN的用户登录信息

    问题: 在VS开发环境下,使用SVN做版本控制,用了TortoiseSVN和插件AnkhSVN.提交代码到SVN服务器时,发现用的是别人的SVN帐号,不是自己的号(比如该电脑之前是别人在使用).想要清 ...

  4. 模仿Struts2的Interceptor拦截器实现

    模仿Struts2的Interceptor拦截器实现 public interface Invocation { public Object invoke(); } public interface ...

  5. Apache HttpComponents 获取页面内容String方式

    /* * ==================================================================== * Licensed to the Apache S ...

  6. uboot中MAC网络(待续)

    start ->start_armboot ->main_loop 实际应用中问题:为什么从nandflash读出的MAC(写到物理phy上)与上层网口地址不同(上层网口采用env的mac ...

  7. JVM监控工具用法指导

    JVM监控工具用法指导 2010-09-27 15:39 dolphin-ygj javaeye.com 字号:T | T 通过JVM监控工具的使用可以及时发现问题,剔除安全隐患,这里向大家描述一下常 ...

  8. 分享10个原生JavaScript技巧

    首先在这里要非常感谢无私分享作品的网友们,这些代码片段主要由网友们平时分享的作品代码里面和经常去逛网站然后查看源文件收集到的.把平时网站上常用的一些实用功能代码片段通通收集起来,方便网友们学习使用,利 ...

  9. oracle权限详解

    一.权限分类:系统权限:系统规定用户使用数据库的权限.(系统权限是对用户而言). 实体权限:某种权限用户对其它用户的表或视图的存取权限.(是针对表或视图而言的). 二.系统权限管理:1.系统权限分类: ...

  10. udhcpc

    /********************************************* * dhcpc * dhcpc是dhcp的客户端,在busybox中实现.今天正好了解一下. * Tony ...