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白皮书只要求在一条赋值语句中呈现为函数调用(包括方法和数据通道接收操作)的表达式(如果它们之间不存在依赖关系)必须按照从左到右的顺序进行估值。除此之外,白皮书并未做其它要求。所以,上面这个程序中的表达式xf(&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 992 2 992 0 99或者0 2 99,取决于具体的编译器实现。

5