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 回复

<</>>* 具有相同的优先级:

优先级    运算符
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

Go 编程语言规范 - Go 编程语言(希望这个链接能正常工作)


在Go语言中,运算符优先级确实是明确的,但你的观察涉及到一个常见的误解。让我们先澄清Go的运算符优先级规则:

Go语言运算符优先级(从高到低)

  1. * / % << >> & &^
  2. + - | ^
  3. == != < <= > >=
  4. &&
  5. ||

问题分析

在你的表达式 (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语言将移位运算符(<<, >>)与算术运算符(*, /, %)放在同一优先级,并按从左到右结合。这种设计选择确实与其他语言不同,因此在移植代码时需要特别注意。

建议在复杂的位运算表达式中总是使用明确的括号,这样可以提高代码的可读性和跨语言一致性。

回到顶部