【问题标题】:What is the result of a single variable expression evaluation单变量表达式求值的结果是什么
【发布时间】:2020-12-17 13:00:20
【问题描述】:

运行以下 Go 代码的 sn-p 可以实现函数 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 中,...参数必须是单值表达式...参数按通常的顺序计算。求值后,调用的参数按值传递给函数...其中usual order从左到右的词法顺序。

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

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

但实际上看起来函数接收到的第一个参数的值是在评估第二个参数时设置的,这有点令人困惑。

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

【问题讨论】:

    标签: go expression specifications


    【解决方案1】:

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

    在函数调用中,函数值和参数在the usual order 中计算。 求值后,将调用的参数按值传递给函数,被调用的函数开始执行。

    评估参数并不意味着它们的值被读取或“获取”。第一个参数是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 上尝试。

    【讨论】:

    • 感谢@icza 的详细解释!对我来说,主要的收获是表达式 s 的计算结果为 s 本身,但不是它的值。这是一种预期的行为,但规范可能没有明确说明。
    猜你喜欢
    • 2021-03-22
    • 2013-05-10
    • 2016-11-04
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多