# Swift 按位和移位运算符

> 原文： [https://www.programiz.com/swift-programming/bitwise-operators](https://www.programiz.com/swift-programming/bitwise-operators)

#### 在本教程中，您将学习 Swift 中的不同按位运算。 这些用于表达式中的位级别计算。

位用于表示二进制数字。 二进制数可以有两个可能的值 0 或 1。作为初学者级程序员，您不必在位级别进行操作。

使用原始数据类型，例如：整数，浮点数，布尔值，字符串等就足够了。 在处理[低级编程](https://en.wikipedia.org/wiki/Low-level_programming_language "Low level programming")时，可能需要在位级工作。

除了[基本运算符](/swift-programming/operators "Swift operators")之外，Swift 还提供了丰富的运算符集来操作位。 这些运算符与逻辑运算符相似，不同之处在于它们使用数据（位）的二进制表示形式工作。

按位运算符是用于更改操作数的各个位的运算符。 操作数是完成操作的变量或常量。

迅速列出所有可用的按位运算符：

* * *

## 1.按位非运算符

它由代字号`~`表示，可以应用于单个操作数。 这会将所有位反转。 即将 1 更改为 0，将 0 更改为 1。

如果`x`是保存二进制值（即 0 或 1）的变量/常数，则`x`变量的按位非运算可以表示在下表中：

| `x` | `~x` |
| --- | --- |
| 0 | 1 |
| 1 | 0 |

* * *

### 示例 1：无符号整数的按位非运算符

```swift
let initalNumber:UInt8 = 1 
let invertedNumber = ~initalNumber 
print(invertedNumber) 
```

当您运行上述程序时，输出将是：

```swift
254
```

在上面的程序中，语句`let initalNumber:UInt8 = 1`的类型为`Unsigned int`，大小为 8 位。 因此，十进制的 1 可以表示为二进制的`00000001`。

按位非运算符更改变量或常量的所有位，位 0 更改为 1，而 1 更改为 0。因此`invertedNumber`包含位`11111110`。 将其转换为十进制后，将其表示为 254。因此，语句`print(invertedNumber)`在屏幕中输出 254。

您还可以直接在位中执行按位运算符，如下所示：

* * *

### 示例 2：以位为单位的按位非运算符

```swift
let initialBits: UInt8 = 0b11111111
let invertedBits = ~initialBits  
print(invertedBits) 
```

运行该程序时，输出为：

```swift
0
```

`initialBits`包含对应于十进制 255 的二进制值`11111111`。 为了用二进制表示数字，我们在字面值中以`0b`作为前缀。 如果没有`0b`作为前缀，它将把它当作普通整数对待，并且会出现溢出错误（`UInt8`只能存储 0 到 255 之间的数字）。

由于我们使用了按位非运算符，因此将所有 1 都更改为 0。因此，常量`initialBits`包含`00000000`，它等于`UInt8`中的 0。

* * *

### 示例 3：带符号整数的按位非运算符

```swift
let initalNumber:Int = 1 
let invertedNumber = ~initalNumber 
print(invertedNumber) 
```

运行该程序时，输出为：

```swift
-2
```

在上面的程序中，十进制的 1 可以表示为`00000001`的二进制。 按位非运算符更改变量或常量的所有位，位 0 更改为 1，1 更改为 0。因此，`invertedNumber`包含位`11111110`。 这应该在屏幕中输出 254。 而是返回-2。 奇怪吧？ 让我们在下面探讨这是如何发生的。

`let initalNumber:Int = 1`是一个可以容纳正整数和负整数的有符号整数。 这就是为什么当我们不对有符号整数应用运算符时，返回的二进制也可能表示负数的原因。

**编译器如何将 -2 解释为二进制形式的`11111110`？**

编译器使用[二进制补码](https://en.wikipedia.org/wiki/Two%27s_complement "Two's complement")表示整数。 要获得整数的二进制补码负号，您应该首先以二进制形式写出数字，然后反转数字，然后在结果中加一个。

**找出 -2** 的二进制补码的步骤：

1.  以二进制形式写 2：`00000010`
2.  反转数字。 0 变成 1，而 1 变成 0：`11111101`
3.  加 1：`11111110`

这就是编译器将二进制数`1111110`解释为十进制`-2`的方式。 但是，编译器有些许变化，我们没有注意到。 它还将`invertedNumber`的类型推断为`Int8`类型。

* * *

为了理解这一点，让我们看下面的例子：

```swift
print(Int8(bitPattern: 0b11111110))
print(0b11111110)
```

运行该程序时，输出为：

```swift
-2
254
```

在上面的示例中，编译器仅对带符号的 8 位整数将二进制数处理为十进制的-2。 因此，语句`print(Int8(bitPattern: 0b11111110))`在屏幕上输出 -2。

但是对于大小为 32/64 位并且可以容纳较大值的普通整数类型，它将其解释为`254`。 因此，语句`print(0b11111110)`在屏幕上输出 **254**。

* * *

## 2.按位与运算符

它由`&`表示，可以应用于两个操作数。 与运算符比较两个位，如果两个位均为 1，则返回 1，否则返回 0。

如果`x`和`y`是变量/常数，且保持二进制值（即 0 或 1），则`x`和`y`的按位与运算如下表所示：

与

| `x` | `y` | `x & y` |
| --- | --- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 1 | 1 |
| 1 | 0 | 0 |

* * *

### 例 5：按位与运算

```swift
let xBits = 0b10000011
let yBits = 0b11111111
let result = xBits & yBits
print("Binary:",String(result, radix: 2))
print(result)
```

运行该程序时，输出为：

```swift
Binary: 10000011
131 
```

在上面的程序中，语句`let result = xBits & yBits`组合了两个操作数`xBits`和`yBits`的位。 如果两个位均为 1，则返回 1，否则返回 0。

`String(value , radix: )`初始化器用于表示不同编号系统中的编号。 如果提供基数 2。它将数字转换为二进制数系统。 同样，十六进制可以使用 16，十进制可以使用 10。

语句`print("Binary:",String(result, radix: 2))`在屏幕上输出`Binary: 10000011`。`10000011`等于十进制的 131，语句`print(result)`在控制台中输出 131。

* * *

## 3.按位或运算符

它表示为`|`，可以应用于两个操作数。 如果按位或运算符的一个或多个输入为 1，则将两个位进行比较，并生成结果 1；否则为 0。

如果`x`和`y`是变量/常数，且保持二进制值，即 0 或 1。则`x`和`y`的按位或运算如下表所示：

或

| `x` | `y` | <code>x &#124; y</code> |
| --- | --- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 1 | 1 |
| 1 | 0 | 1 |

* * *

### 例 6：按位或运算

```swift
let xBits = 0b10000011
let yBits = 0b11111111
let result = xBits | yBits
print("Binary:", String(result, radix: 2))
print(result) 
```

运行该程序时，输出为：

```swift
Binary: 11111111
255 
```

在上述程序中，语句`let result = xBits | yBits`组合了两个常量`xBits`和`yBits`的位。 如果任何位为 1，则返回 1，否则返回 0。

语句`print("Binary:",String(result, radix: 2))`在屏幕上输出`Binary: 11111111`。 由于`11111111`等于十进制的`255`，因此语句`print(result)`在屏幕上输出 **255** 。

* * *

## 4.按位异或运算符

它表示为`^`，可以应用于两个操作数。 如果异或运算符的输入之一恰好是 1，则异或运算符将比较两个位并生成结果 1，否则返回 0。

如果`x`和`y`是变量/常数，且保持二进制值，即 0 或 1。则`x`和`y`的按位异或运算如下表所示：

异或

| `x` | `y` | `x ^ y` |
| --- | --- | --- |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 1 | 0 |
| 1 | 0 | 1 |

* * *

### 示例 7：按位异或操作

```swift
let xBits = 0b10000011
let yBits = 0b11111111
let result = xBits ^ yBits
print("Binary:", String(result, radix: 2))
print(result) 
```

运行该程序时，输出为：

```swift
Binary: 1111100
124

```

在上述程序中，语句`let result = xBits ^ yBits`组合了两个常量`xBits`和`yBits`的位。 如果正好一位为 1，则返回 1，否则返回 0。

语句`print("Binary:",String(result, radix: 2))`在屏幕上输出`Binary：1111100`（相当于 01111100）。 由于`1111100`等于十进制的`124`，因此语句`print(result)`在屏幕上输出 **124**。

* * *

## 5.按位移位运算符

该运算符用于将数字中的所有位向左或向右移动一定数量的位置，并且可以应用于单个操作数。 它表示为`<<`或`>>`。

移位运算符有两种：

* * *

### 按位左移运算符

*   表示为`<<`
*   这会导致这些位向左移动，由`<<`后面的数字指定。
*   移位操作已腾空的位置用零填充。
*   将整数左移一位将其值加倍

* * *

#### 例 8：按位左移运算符

```swift
let someBits:UInt8 = 0b11000100
print(someBits << 1) 
```

运行该程序时，输出为：

```swift
136
```

在上面的程序中，我们使用了左移运算符。 使用`<<` 1 表示将位向左移动 1。 这些数字向左移动一个位置，右侧的最后一个数字填充零。

您还可以看到丢失的数字**从左侧“移出”**。 它不会从右侧再次缠绕。 将其向左移动一位将二进制文件中的 1 删除，向右添加 0 以填充移位后的值，其余的其他位则向左移动 1。

这将返回`10001000`，它等效于`UInt8`中的`136`。 因此，`print(someBits << 1)`语句在屏幕上输出 **136**。

* * *

### 按位右移运算符

*   表示为`>>`
*   这会导致这些位向右移动数字，后跟`>>`
*   对于无符号数字，移位操作已腾空的位位置为零。
*   对于带符号的数字（也可以为负的数字），符号位用于填充腾出的位位置。 换句话说，如果数字为正数，则使用 0；如果数字为负数，则使用 1。
*   向右移动一个位置可将其值减半。

* * *

#### 示例 9：无符号整数的按位右移运算符

```swift
let someBits: UInt8 = 4     
print(someBits >> 1) 
```

运行该程序时，输出为：

```swift
2
```

在上面的程序中，我们对无符号整数使用了右移运算符。 使用`>> 1`表示将位右移 1。 移位操作已腾空的位位置始终在无符号整数上填充零。

由于 4 以二进制形式表示为`00000100`。 向右移动一位，返回`00000010`，它等效于`UInt8`中的`2`。 因此，`print(someBits >> 1)`语句在屏幕上输出 2。

* * *

#### 示例 10：有符号整数的按位右移运算符

```swift
let someBits:Int = -4     
print(someBits >> 1) 
```

运行该程序时，输出为：

```swift
-2
```

在上面的程序中，我们对无符号整数使用了右移运算符。 与正数不同，对负数使用`>>`，使用 1 填充空位，而不是 0。

由于`-4`用二进制表示为`11111100`。 将其向右移动一位并将 1 放置在空闲位置，将返回`11111110`，与`Int8`类型的`-2`等效。 因此，`print(someBits >> 1)`语句在屏幕上输出 -2。