Go语言中的运算符优先级(OO)问题探讨
Go语言中的运算符优先级(OO)问题探讨 可能并非由于面向对象,而是其他原因。在尝试实现Pat Morin的基数排序实现时,我遇到了一个涉及第一个for循环表达式的问题:
c[(a[i] >> d*p)&((1<<d)-1)]++;
似乎在Go语言中,表达式的前半部分 (a[i] >> d*p) 是这样求值的:((a[i] >> d) * p),这与Java、Python和C等语言不同,后者是这样求值的:(a[i] >> (d * p))。我还没有测试表达式的后半部分是如何求值的(已经很晚了),但我想情况是一样的。Swift似乎遵循与Go相同的运算顺序逻辑。
我的问题是,这是有意为之的吗?我原以为按照Go的运算顺序,乘法会先执行。我感觉我遗漏了什么,或者也许这就是Go的处理方式。
2 回复
在Go语言中,运算符优先级确实是明确的,但你的观察涉及到一个常见的误解。让我们先澄清Go的运算符优先级规则:
Go语言运算符优先级(从高到低)
* / % << >> & &^+ - | ^== != < <= > >=&&||
问题分析
在你的表达式 (a[i] >> d*p) 中:
>>和*具有相同的优先级(第1级)- 相同优先级的运算符按从左到右的顺序结合
因此,a[i] >> d*p 在Go中确实被解析为 (a[i] >> d) * p。
示例验证
package main
import "fmt"
func main() {
a := []uint32{0xFF} // 255
i := 0
d := uint(2)
p := uint(2)
// 原始表达式
result1 := (a[i] >> d*p)
fmt.Printf("(a[i] >> d*p) = %d\n", result1) // (255 >> 2) * 2 = 63 * 2 = 126
// 你期望的表达式
result2 := (a[i] >> (d*p))
fmt.Printf("(a[i] >> (d*p)) = %d\n", result2) // 255 >> 4 = 15
// 验证后半部分
mask1 := ((1<<d)-1)
fmt.Printf("((1<<d)-1) = %d\n", mask1) // (1<<2)-1 = 3
mask2 := (1<<(d)-1) // 注意:这实际上是 (1<<d)-1,因为<<优先级高于-
fmt.Printf("(1<<(d)-1) = %d\n", mask2) // 同样是3
// 完整表达式对比
full1 := (a[i] >> d*p) & ((1<<d)-1)
full2 := (a[i] >> (d*p)) & ((1<<(d))-1)
fmt.Printf("原始: %d, 期望: %d\n", full1, full2)
}
与C/Java的比较
C、Java、Python等语言中,>>的优先级低于乘法*,所以:
- C/Java:
a[i] >> d*p=a[i] >> (d*p) - Go:
a[i] >> d*p=(a[i] >> d) * p
解决方案
对于你的基数排序实现,需要显式添加括号:
// 修改前
c[(a[i] >> d*p)&((1<<d)-1)]++
// 修改后
c[(a[i] >> (d*p)) & ((1<<d)-1)]++
或者更清晰的方式:
shift := d * p
mask := (1 << d) - 1
index := (a[i] >> shift) & mask
c[index]++
结论
这不是Go语言的bug,而是设计上的差异。Go语言将移位运算符(<<, >>)与算术运算符(*, /, %)放在同一优先级,并按从左到右结合。这种设计选择确实与其他语言不同,因此在移植代码时需要特别注意。
建议在复杂的位运算表达式中总是使用明确的括号,这样可以提高代码的可读性和跨语言一致性。


