Go语言运算符全解析
基本运算符
算术运算符
+ 加法:
- 数值相加:
3 + 5
结果为 8 - 字符串拼接:
"Hello" + " " + "World"
结果为"Hello World"
- 示例:
sum := 10 + 20 // sum = 30
- 数值相加:
- 减法:
a - b
计算两个数的差- 示例:
diff := 15 - 7 // diff = 8
* 乘法:
a * b
计算两个数的乘积- 示例:
product := 3 * 4 // product = 12
/ 除法:
- 整数除法会截断小数部分:
5 / 2 = 2
- 浮点数除法保留小数:
5.0 / 2 = 2.5
- 示例:
quotient := 10 / 3 // quotient = 3
- 整数除法会截断小数部分:
% 取模:
- 求余数:
5 % 2 = 1
- 示例:
remainder := 10 % 3 // remainder = 1
- 求余数:
++ 自增:
- 只能作为语句:
i++
- 不能作为表达式:
x = i++
是非法语法 - 示例:
i := 0 i++ // i = 1
- 只能作为语句:
-- 自减:
- 只能作为语句:
i--
- 示例:
j := 5 j-- // j = 4
- 只能作为语句:
关系运算符
== 等于:
- 比较两个值是否相等:
a == b
- 示例:
isEqual := (10 == 10) // isEqual = true
- 比较两个值是否相等:
!= 不等于:
- 示例:
isNotEqual := (10 != 5) // isNotEqual = true
- 示例:
> 大于:
- 示例:
isGreater := (15 > 10) // isGreater = true
- 示例:
< 小于:
- 示例:
isLess := (3 < 5) // isLess = true
- 示例:
>= 大于等于:
- 示例:
isGE := (10 >= 10) // isGE = true
- 示例:
<= 小于等于:
- 示例:
isLE := (5 <= 10) // isLE = true
- 示例:
逻辑运算符
&& 逻辑与:
- 短路运算:
false && expr
不计算右边表达式 - 示例:
if x > 0 && y < 10 {// 只有x>0且y<10都为真时才执行 }
- 短路运算:
|| 逻辑或:
- 短路运算:
true || expr
不计算右边表达式 - 示例:
if x == 0 || y == 0 {// x等于0或y等于0时执行 }
- 短路运算:
! 逻辑非:
- 取反操作:
!true
为false
- 示例:
isValid := false if !isValid {// 当isValid为false时执行 }
- 取反操作:
位运算符
& 按位与:
- 示例:
0b1010 & 0b1100 = 0b1000
(即 10 & 12 = 8)
- 示例:
| 按位或:
- 示例:
0b1010 | 0b1100 = 0b1110
(即 10 | 12 = 14)
- 示例:
^ 按位异或:
- 示例:
0b1010 ^ 0b1100 = 0b0110
(即 10 ^ 12 = 6)
- 示例:
<< 左移位:
- 示例:
1 << 3
得 8 (二进制 0001 左移3位变为 1000)
- 示例:
>> 右移位:
- 示例:
8 >> 2
得 2 (二进制 1000 右移2位变为 0010)
- 示例:
&^ 位清除(AND NOT):
- 示例:
0b1010 &^ 0b1100 = 0b0010
(即 10 &^ 12 = 2)
- 示例:
赋值运算符
= 基本赋值:
- 示例:
x = 10
- 示例:
复合赋值:
+=
:x += 5
等价于x = x + 5
-=
:x -= 3
等价于x = x - 3
*=
:x *= 2
等价于x = x * 2
/=
:x /= 4
等价于x = x / 4
%=
:x %= 3
等价于x = x % 3
<<=
:x <<= 1
等价于x = x << 1
>>=
:x >>= 2
等价于x = x >> 2
&=
:x &= 0xF
等价于x = x & 0xF
^=
:x ^= 0xFF
等价于x = x ^ 0xFF
|=
:x |= 0x0F
等价于x = x | 0x0F
特殊运算符
指针运算符
& 取地址:
- 获取变量内存地址:
&x
- 示例:
x := 10 ptr := &x // ptr现在保存x的内存地址
- 获取变量内存地址:
* 解引用:
- 访问指针指向的值:
*ptr
- 示例:
x := 10 ptr := &x fmt.Println(*ptr) // 输出10
- 访问指针指向的值:
通道运算符
- <- 用于通道的发送和接收:
发送:
ch <- value
将value发送到通道chch := make(chan int) go func() {ch <- 42 // 发送42到通道 }()
接收:
value := <-ch
从通道ch接收值received := <-ch // 从通道接收值 fmt.Println(received) // 输出42
类型断言运算符
- .(type) 用于接口类型断言:
类型判断:
var val interface{} = "hello" if s, ok := val.(string); ok {fmt.Println(s) // 输出"hello" }
类型开关:
switch v := x.(type) { case int:fmt.Println("int:", v) case string:fmt.Println("string:", v) default:fmt.Println("unknown type") }
内存分配运算符
new:
- 分配零值内存并返回指针:
ptr := new(int)
- 示例:
p := new(int) // p是*int类型,指向的int值为0 *p = 10 // 修改指针指向的值
- 分配零值内存并返回指针:
make:
- 用于slice、map和channel的初始化
- 示例:
s := make([]int, 10) // 长度10的slice m := make(map[string]int) // 空map ch := make(chan int, 5) // 缓冲区大小5的通道
运算符优先级
优先级从高到低:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
示例:
result := 5 + 3 * 2 // 结果为11,因为乘法优先级高于加法
优先级括号明确:
result := (5 + 3) * 2 // 结果为16,括号改变优先级
运算符重载
Go 语言不支持运算符重载。替代方案:
使用方法调用实现类似功能,例如实现 Add 方法代替 + 运算符:
type Vector struct { X, Y float64 }func (v Vector) Add(u Vector) Vector {return Vector{v.X + u.X, v.Y + u.Y}
}func main() {v1 := Vector{1, 2}v2 := Vector{3, 4}sum := v1.Add(v2) // 结果为Vector{4, 6}
}
实际应用示例
算术运算符
// 计算圆的面积
radius := 5.0
area := math.Pi * radius * radius
fmt.Printf("Area: %.2f\n", area) // 输出: Area: 78.54// 计算两点间距离
func distance(x1, y1, x2, y2 float64) float64 {dx := x2 - x1dy := y2 - y1return math.Sqrt(dx*dx + dy*dy)
}
位运算符优化
// 快速判断奇偶
isEven := num & 1 == 0 // 如果num是偶数则为true// 乘以2的幂次
result := num << 3 // 等同于 num * 8// 交换两个变量的值(不使用临时变量)
a, b := 10, 20
a ^= b
b ^= a
a ^= b // 现在a=20, b=10// 检查是否为2的幂次
isPowerOfTwo := n > 0 && (n & (n - 1)) == 0
通道运算符
// 并发处理多个任务
func worker(id int, jobs <-chan int, results chan<- int) {for j := range jobs {time.Sleep(time.Second) // 模拟耗时操作results <- j * 2}
}func main() {jobs := make(chan int, 100)results := make(chan int, 100)// 启动3个workerfor w := 1; w <= 3; w++ {go worker(w, jobs, results)}// 发送9个任务for j := 1; j <= 9; j++ {jobs <- j}close(jobs)// 收集结果for a := 1; a <= 9; a++ {fmt.Println(<-results)}
}
常见错误与陷阱
整数除法
a := 5 / 2 // 结果为2,不是2.5// 正确做法:
b := float64(5) / 2 // 2.5// 常见错误场景:
percentage := part / total * 100 // 如果part和total都是整数,结果可能为0
// 正确应为:
percentage := float64(part) / float64(total) * 100
逻辑短路
if p != nil && *p == 10 { // 安全访问指针,因为如果p为nil,不会执行*p
}// 错误示范:
if *p == 10 && p != nil { // 可能导致panic
}
nil 指针
var p *int
*p = 10 // 运行时panic,因为p是nil// 安全做法:
if p != nil {*p = 10
}// 或者使用new初始化:
p := new(int)
*p = 10 // 安全
优先级错误
if a == 1 || b == 2 && c == 3 { ... }
// 实际解析为:a == 1 || (b == 2 && c == 3)
// 可能意图是:(a == 1 || b == 2) && c == 3// 正确做法是明确使用括号:
if (a == 1 || b == 2) && c == 3 { ... }
性能优化建议
位运算替代算术运算
// 用位运算代替乘除2的幂次
x = x << 1 // 代替 x * 2
x = x >> 1 // 代替 x / 2// 取模运算优化
remainder := x & (n-1) // 代替 x % n,当n是2的幂次时// 判断符号位
isNegative := x >> 31 // 对于32位整数
避免高开销运算符
// 减少不必要的浮点运算
// 错误示例:
for i := 0; i < n; i++ {x := float64(i) * 0.1 // 每次循环都做浮点转换
}// 优化为:
step := 0.1
for i := 0; i < n; i++ {x := float64(i) * step // 只做一次浮点转换
}// 避免频繁的内存分配
// 错误示例:
for i := 0; i < 1000; i++ {s := make([]byte, 1024) // 每次循环都分配新内存// 使用s...
}// 优化为:
buf := make([]byte, 1024)
for i := 0; i < 1000; i++ {// 重用buf// 使用buf...
}
编译器优化
// 使用常量表达式让编译器优化
const mask = 1<<10 - 1 // 编译时计算// 错误示例:
var mask = 1<<10 - 1 // 运行时计算// 循环展开优化
const size = 4
var sum int
for i := 0; i < size; i++ {sum += i
}
// 编译器可能会展开为:
sum = 0 + 1 + 2 + 3
通道操作优化
// 批量处理优于单次操作
// 低效:
for i := 0; i < 1000; i++ {ch <- i // 1000次通道操作
}// 优化为:
batch := make([]int, 1000)
for i := 0; i < 1000; i++ {batch[i] = i
}
ch <- batch // 1次通道操作// 合理设置通道缓冲区大小
// 无缓冲(同步):
ch1 := make(chan int) // 发送和接收必须同时准备好// 有缓冲(异步):
ch2 := make(chan int, 100) // 可以缓冲100个元素