单变量表达式求值的结果是什么
运行以下 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 = %dn", s, p)
}
func main() {
var s int
foo(s, setVal(&s, 99))
}
程序输出s = 99, p = 0,这意味着变量的修改值s已传递给函数。
以下是 Go 规范对案例的说明。
在函数调用中,...arguments 必须是单值表达式 ... 参数按通常的顺序计算。他们评估后,调用的参数是按值传递给函数...如果通常的顺序是词法左到右的顺序。
甲变量是用于保持的值的存储位置。...通过引用表达式中的变量来检索变量的值;它是分配给变量的最新值。
因此foo(s, setVal(&s, 99))是函数调用,变量s和函数setVal()是单值表达式,s先求值。最后一个规范语句假设变量评估的结果是它的值,所以如果这是真的,函数foo应该接收变量的初始值s。
但实际上看起来该函数接收的是在评估第二个参数时设置的第一个参数的值,这有点令人困惑。
这是否意味着评估顺序被破坏或变量评估的结果不是它的值?
回答
你从规范中“错过”的是规范:调用:
在函数调用中,函数值和参数按通常的顺序计算。在对它们求值后,调用的参数按值传递给函数,被调用的函数开始执行。
评估参数并不意味着它们的值被读取或“采用”。第一个参数是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()被调用,它的返回值将是函数值 - 参数被评估: (a)
first()被调用,其返回值被取消引用;(b)setVal()被调用,其返回值将被使用 - 现在取值:的值
*s和旧值s(由 返回的值setVal())。
这将输出与您的示例相同的输出,请在Go Playground上尝试。