单变量表达式求值的结果是什么

What is the result of a single variable expression evaluation

运行 下面的 Go 代码片段可以实现函数 foo 在评估函数的第二个参数时接收实际设置的第一个参数的值。这种行为可能看起来违反直觉,因此我们需要证明这是语言规范的一部分,而不是特定于实现的东西。

package main

import (
    "fmt"
)

func setVal(s *int, v int) int {
    old := *s
    *s = v
    return old
}

func foo(s int, p int) {
    fmt.Printf("s = %d, p = %d\n", s, p)
}

func main() {
    var s int
    foo(s, setVal(&s, 99))
}

程序输出 s = 99, p = 0,这意味着变量 s 的修改值已传递给函数。

这是 Go 规范对这个案例的描述。

function call, ...arguments must be single-valued expressions ... arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function... Where usual order 中是 词汇从左到右的顺序。

一个variable是一个存放值的存储位置。 ...通过引用表达式中的变量来检索变量的值;它是分配给变量的最新值。

因此foo(s, setVal(&s, 99))是函数调用,变量s和函数setVal()是单值表达式,s先求值。最后一个规范声明假设变量评估的结果是它的值,所以如果这是真的,函数 foo 应该接收变量 s.

的初始值

但实际上函数似乎接收了在计算第二个参数时设置的第一个参数的值,这有点令人困惑。

这是否意味着评估顺序被破坏或变量评估的结果不是它的值?

您从规范中“错过”的是 Spec: Calls:

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.

评估参数并不意味着读取或“获取”它们的值。第一个参数是 s,它的求值是 s 本身,但它的值还没有被读取。第二个参数被评估,这意味着 setVal() 被调用并将修改 s.

的值

现在我们已经评估了参数,读取它们的值,因此 s 将具有值 99

计算示例中的 s 是微不足道的,但当然这可能是一个更复杂的表达式,就像第二个参数一样。这是一个更复杂的例子:

s, s2 := new(int), new(int)

getFunc := func() func(s int, p int) { return foo }
first := func(a, b *int) *int { return a }

getFunc()(*first(s, s2), setVal(s, 99))

最后一个函数的调用涉及以下步骤:

  • 函数值被计算:getFunc()被调用,它的return值将是函数值
  • 评估参数: (a) first() 被调用,它的 return 值被取消引用; (b) setVal() 被调用,它的 return 值将被使用
  • 现在取值:*s 的值和 s 的旧值(值 return 由 setVal() 编辑)。

这将输出与您的示例相同的结果,请在 Go Playground.

上尝试