导航菜单

运算符

运算符(Operator)

运算符是用于执行运算的符号,它对一个或多个操作数进行操作,产生一个结果。Go 语言提供了丰富的运算符,包括算术、关系、逻辑、位运算和赋值运算符。

算术运算符

算术运算符用于执行基本的数学运算:

运算符描述示例
+加法3 + 2 = 5
-减法3 - 2 = 1
*乘法3 * 2 = 6
/除法7 / 2 = 3(整数除法截断小数部分)
%取模(取余)7 % 2 = 1
++自增(仅后置)x++ 等价于 x = x + 1
--自减(仅后置)x-- 等价于 x = x - 1
a, b := 10, 3

fmt.Println(a + b)  // 13
fmt.Println(a - b)  // 7
fmt.Println(a * b)  // 30
fmt.Println(a / b)  // 3(整数除法,截断小数部分)
fmt.Println(a % b)  // 1

// 浮点数除法
fmt.Println(10.0 / 3.0)  // 3.3333333333333335

自增与自减

Go 的 ++-- 语句与 C/Java 有重要区别:

x := 10

// Go 中 ++ 和 -- 是语句,不是表达式
x++     // 正确:x 变为 11
x--     // 正确:x 变为 10

// 以下用法在 Go 中是非法的:
// y := x++     // 编译错误:++ 不是表达式
// if x++ > 0 {} // 编译错误
// fmt.Println(x++) // 编译错误

// 正确的写法:
x++
if x > 0 {}

关系运算符

关系运算符用于比较两个值,返回 bool 结果:

运算符描述示例
==等于5 == 5true
!=不等于5 != 3true
<小于3 < 5true
>大于5 > 3true
<=小于或等于3 <= 3true
>=大于或等于5 >= 5true
a, b := 10, 20

fmt.Println(a == b)  // false
fmt.Println(a != b)  // true
fmt.Println(a < b)   // true
fmt.Println(a > b)   // false
fmt.Println(a <= b)  // true
fmt.Println(a >= b)  // false

字符串比较

Go 中字符串可以使用关系运算符比较,按字典序(Unicode 码点顺序)比较:

fmt.Println("abc" < "abd")     // true
fmt.Println("abc" == "abc")    // true
fmt.Println("ABC" < "abc")     // true(大写字母的 Unicode 码点小于小写字母)
fmt.Println("你好" < "世界")   // true

逻辑运算符

逻辑运算符用于操作布尔值:

运算符描述示例
&&逻辑与(AND)true && falsefalse
||逻辑或(OR)true || falsetrue
!逻辑非(NOT)!truefalse
a, b := true, false

fmt.Println(a && b)   // false
fmt.Println(a || b)   // true
fmt.Println(!a)       // false
fmt.Println(!b)       // true

短路求值

Go 的 &&|| 具有短路求值(Short-circuit evaluation)特性:

// && 短路:如果左侧为 false,右侧不再计算
var result bool
result = false && someExpensiveFunction()  // someExpensiveFunction 不会被调用

// || 短路:如果左侧为 true,右侧不再计算
result = true || someExpensiveFunction()  // someExpensiveFunction 不会被调用
// 利用短路求值防止空指针
if user != nil && user.Name == "Alice" {
    fmt.Println("Hello, Alice!")
}

// 如果 user 为 nil,&& 左侧为 false,右侧不会被求值,不会 panic

位运算符

位运算符直接对整数的二进制位进行操作:

运算符描述示例
&按位与(AND)5 & 31
|按位或(OR)5 | 37
^按位异或(XOR)5 ^ 36
&^按位清除(AND NOT)5 &^ 34
<<左移1 << 38
>>右移8 >> 31

按位与(&)

两个对应位都为 1 时结果才为 1

  0101  (5)
& 0011  (3)
-------
  0001  (1)
fmt.Println(5 & 3)   // 1
fmt.Println(0xFF & 0x0F)  // 15

按位或(|)

两个对应位有一个为 1 时结果就为 1

  0101  (5)
| 0011  (3)
-------
  0111  (7)
fmt.Println(5 | 3)   // 7

按位异或(^)

两个对应位不同时结果为 1,相同时为 0

  0101  (5)
^ 0011  (3)
-------
  0110  (6)
fmt.Println(5 ^ 3)   // 6
fmt.Println(5 ^ 5)   // 0(相同异或为零)

按位清除(&^)

&^ 是 Go 特有的运算符,也称为”位清除”。对于右侧为 1 的位,将左侧对应位清零;右侧为 0 的位,保持左侧不变:

  0101  (5)
&^0011  (3)
-------
  0100  (4)
fmt.Println(5 &^ 3)   // 4
// 解释:右侧 3 的二进制为 0011,第0位和第1位为1,所以清除左侧的第0位和第1位

移位运算(<<, >>)

左移 &lt;&lt; 将二进制位向左移动指定位数,右边补零;右移 &gt;&gt; 将二进制位向右移动指定位数:

1 &lt;&lt; 3:
  0001 → 1000  (8)

8 &gt;&gt; 3:
  1000 → 0001  (1)
fmt.Println(1 << 0)  // 1
fmt.Println(1 << 1)  // 2
fmt.Println(1 << 2)  // 4
fmt.Println(1 << 3)  // 8
fmt.Println(1 << 10) // 1024

赋值运算符

赋值运算符用于给变量赋值:

运算符描述等价写法
=赋值x = 5
+=加后赋值x += 5x = x + 5
-=减后赋值x -= 5x = x - 5
*=乘后赋值x *= 5x = x * 5
/=除后赋值x /= 5x = x / 5
%=取模后赋值x %= 5x = x % 5
&=按位与后赋值x &= 5x = x & 5
|=按位或后赋值x |= 5x = x | 5
^=按位异或后赋值x ^= 5x = x ^ 5
<<=左移后赋值x <<= 2x = x << 2
>>=右移后赋值x >>= 2x = x >> 2
x := 10
x += 5   // x = 15
x -= 3   // x = 12
x *= 2   // x = 24
x /= 4   // x = 6
x %= 4   // x = 2

y := 0b1100  // 12
y &= 0b1010  // y = 8  (1000)
y |= 0b0101  // y = 13 (1101)
y ^= 0b1111  // y = 2  (0010)

运算符优先级

Go 的运算符优先级从高到低如下表所示:

优先级运算符结合性
5* / % << >> & &^左结合
4+ - | ^左结合
3== != < <= > >=左结合
2&&左结合
1||左结合
// 不推荐的写法(依赖优先级)
result := a + b * c

// 推荐的写法(明确意图)
result := a + (b * c)

// 逻辑运算推荐加括号
if (x > 0) && (y > 0) && (z > 0) {
    // ...
}

练习题

练习 1:位运算判断奇偶

不使用 % 运算符,仅使用位运算判断一个整数是奇数还是偶数。

参考答案

解题思路:一个整数的二进制最低位决定了它是奇数还是偶数。最低位为 1 则是奇数,为 0 则是偶数。可以用 & 1 来检查最低位。

代码

package main

import "fmt"

func isOdd(n int) bool {
    return n&1 == 1
}

func isEven(n int) bool {
    return n&1 == 0
}

func main() {
    fmt.Println(isOdd(5))   // true
    fmt.Println(isOdd(8))   // false
    fmt.Println(isEven(5))  // false
    fmt.Println(isEven(8))  // true
    fmt.Println(isOdd(-3))  // true(负数同样适用)
}

解释n & 1 只保留 n 的最低位。如果最低位是 1,则 n 是奇数;如果是 0,则 n 是偶数。位运算比取模运算效率更高。

练习 2:位标志操作

使用位运算实现一个简单的权限系统。定义以下权限:读(1)、写(2)、执行(4)。实现添加权限、删除权限、检查权限的功能。

参考答案

解题思路:使用按位或 | 添加权限,使用按位清除 &^ 删除权限,使用按位与 & 检查权限。

代码

package main

import "fmt"

const (
    Read    = 1 << iota  // 1(0001)
    Write                 // 2(0010)
    Execute               // 4(0100)
)

// 添加权限
func addPermission(permissions, perm int) int {
    return permissions | perm
}

// 删除权限
func removePermission(permissions, perm int) int {
    return permissions &^ perm
}

// 检查是否拥有某个权限
func hasPermission(permissions, perm int) bool {
    return permissions&perm == perm
}

func main() {
    var perms int = 0

    // 添加读和执行权限
    perms = addPermission(perms, Read)
    perms = addPermission(perms, Execute)
    fmt.Printf("权限: %04b\n", perms)  // 0101

    // 检查权限
    fmt.Println("可读:", hasPermission(perms, Read))    // true
    fmt.Println("可写:", hasPermission(perms, Write))   // false
    fmt.Println("可执行:", hasPermission(perms, Execute)) // true

    // 删除执行权限,添加写权限
    perms = removePermission(perms, Execute)
    perms = addPermission(perms, Write)
    fmt.Printf("权限: %04b\n", perms)  // 0011

    fmt.Println("可读:", hasPermission(perms, Read))    // true
    fmt.Println("可写:", hasPermission(perms, Write))   // true
    fmt.Println("可执行:", hasPermission(perms, Execute)) // false
}

练习 3:逻辑运算与短路求值

以下代码的输出是什么?请解释每一步的执行过程。

package main

import "fmt"

func main() {
    x := 5
    y := 0

    result := x > 3 && y != 0 && x/y > 1
    fmt.Println(result)

    result2 := x > 3 || x/y > 1
    fmt.Println(result2)
}
参考答案

解题思路:利用逻辑运算符的短路求值特性分析执行过程。

输出

false
true

解释

  1. result := x > 3 && y != 0 && x/y > 1

    • 先计算 x > 3,结果为 true
    • 短路未触发,继续计算 y != 0,结果为 false
    • 由于 && 左侧为 false,触发短路,不会计算 x/y > 1,避免了除零错误
    • 最终结果:false
  2. result2 := x > 3 || x/y > 1

    • 先计算 x > 3,结果为 true
    • 由于 || 左侧为 true,触发短路,不会计算 x/y > 1,同样避免了除零错误
    • 最终结果:true

关键知识点:短路求值不仅可以提高性能,还可以用来防止运行时错误。

搜索