Go中某些语句中的表达式之间的相对估值顺序未定义
以下文章来源于Go 101 ,作者老貘
Go语言中有很多独特的设计,也有很多从其它语言借鉴过来的设计。总体上看,Go和C语言的血缘最近。事实上,我们可以将Go语言称为C+语言。相对于C语言,Go语言的一大特点是尽可能地减少了未定义行为。
但是,完全消除未定义行为的代价是很高的。所以Go中还是有一些白皮书或者官方文档中未明确定义的行为的。这导致了一些具体的编译器实现之间存在着某些行为差异。
举个例子。你觉得下面这个程序会输出什么?
package main
import "fmt"
func f(p *int) int {
*p = 123
return *p
}
func main() {
var x int
y, z := x, f(&x)
fmt.Println(y, z) // ??
}
很多程序员会认为此程序会输出0 123
。这正是gccgo编译器v8.1.0的输出结果。但是对于标准Go编译器v1.12来说,此程序的输出为
123 123
。
<br></br>
哪个编译器的结果是对的?事实上,两者的结果都是正确的。或者说此程序写得有问题和不专业,导致它的输出结果是依赖于具体编译器实现的。<br></br>
<br></br>
Go白皮书只要求在一条赋值语句中呈现为函数调用(包括方法和数据通道接收操作)的表达式(如果它们之间不存在依赖关系)必须按照从左到右的顺序进行估值。除此之外,白皮书并未做其它要求。所以,上面这个程序中的表达式
x
和
f(&x)
的相对估值顺序取决于具体编译器实现。这就是为什么
表达式x
被估值为0
或者
123
都是允许的原因。
<br></br>
下面是另一个例子。请问下面这个(不专业的)程序会输出什么?<br></br>
package main
import "fmt"
func testChange() (int, int, int) {
m := 0
i := 0
f := func() int {
m = 2
i = 2
return 99
}
return m, i, f()
}
func main() {
m, i, j := testChange()
fmt.Println(m, i, j)
}
是的,它可能输出0 0 99
、2 2 99
、
2 0 99
或者
0 2 99
,取决于具体的编译器实现。