Golang程序执行流程深度解析

Golang程序执行流程深度解析

func main() {

i, j := 10, 10

var sum1, sum2 int

sum1 = (i / 2) + think(&i)

sum2 = think(&j) + (i / 2)

fmt.Printf(“Value of sum1 is %d and sum2 is %d”, sum1, sum2)

}

func think(k *int) int {

*k += 4

return 3*(*k) - 1

}

这个简短程序的输出是 sum1=sum2=48。Go 是如何进行求值的?

2 回复

你好,欢迎!Go 语言是急切求值且静态的,但并非一个真正确定性的编程语言。所以结果是 48 和 48,因为 think(&1)(i/2) 之前执行,或者更准确地说:sum1 = (7) + 41。对于 sum2 情况相同。Go 并不强制规定求和表达式中左右操作数的求值顺序。这种行为似乎容易出错,但会极大地促使你编写更清晰的代码,分解复杂的语句和表达式。

更多信息请参阅此链接

func main() {
    fmt.Println("hello world")
}

更多关于Golang程序执行流程深度解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,函数参数和表达式的求值顺序是未定义的,但操作数的求值顺序是从左到右。不过对于同一个表达式中的多个函数调用,Go并没有规定它们的求值顺序。让我们分析这个具体案例:

package main

import "fmt"

func main() {
    i, j := 10, 10
    var sum1, sum2 int
    
    // 关键在这里:表达式求值顺序
    sum1 = (i / 2) + think(&i)  // 情况1
    sum2 = think(&j) + (j / 2)  // 情况2(注意:应该是j/2,不是i/2)
    
    fmt.Printf("Value of sum1 is %d and sum2 is %d", sum1, sum2)
}

func think(k *int) int {
    *k += 4
    return 3*(*k) - 1
}

实际上,原代码有个笔误,应该是:

sum2 = think(&j) + (j / 2)  // 不是 (i / 2)

执行流程分析:

对于sum1的计算:

  1. (i / 2) 先求值:10 / 2 = 5
  2. think(&i) 后求值:
    • *k += 4i = 10 + 4 = 14
    • return 3*14 - 1 = 42 - 1 = 41
  3. sum1 = 5 + 41 = 46

对于sum2的计算:

  1. think(&j) 先求值:
    • *k += 4j = 10 + 4 = 14
    • return 3*14 - 1 = 42 - 1 = 41
  2. (j / 2) 后求值:14 / 2 = 7
  3. sum2 = 41 + 7 = 48

但实际输出是sum1=sum2=48,这说明:

在Go的当前实现中,对于表达式 (i / 2) + think(&i)

  • 操作数从左到右求值,所以 (i / 2) 先计算
  • 但函数调用 think(&i) 中的副作用(修改i的值)不会影响已经计算过的 (i / 2)

然而,如果原代码确实是 think(&j) + (i / 2),那么:

  • think(&j) 修改的是j,不影响i
  • (i / 2) 中的i仍然是10
  • 结果应该是 41 + 5 = 46

要得到sum1=48,需要:

sum1 = (i / 2) + think(&i)  // 假设think(&i)先执行
// think(&i)先执行:i变为14,返回41
// 然后(i/2):14/2=7
// 结果:7+41=48

关键点:

  1. Go不保证二元操作符两边的求值顺序
  2. 但保证操作数自身的求值顺序
  3. 实际编译器中,通常从左到右求值

验证示例:

package main

import "fmt"

func main() {
    // 演示求值顺序
    x := 10
    result := f(&x) + g(&x)
    fmt.Printf("Result: %d, x: %d\n", result, x)
}

func f(p *int) int {
    *p += 5
    fmt.Printf("f called, *p=%d\n", *p)
    return *p
}

func g(p *int) int {
    *p *= 2
    fmt.Printf("g called, *p=%d\n", *p)
    return *p
}
// 输出顺序可能不同,结果也不同

结论: 原程序输出sum1=sum2=48表明在当前Go实现中:

  • 对于 sum1think(&i)(i / 2) 之前执行
  • 对于 sum2think(&j)(j / 2) 之前执行
  • 但这是编译器实现细节,不是语言规范保证的

编写Go代码时,不应依赖这种未定义的求值顺序,有副作用的表达式应该分开写:

// 明确的顺序
temp1 := think(&i)
sum1 = (i / 2) + temp1

// 或者
temp2 := think(&j)
sum2 = (j / 2) + temp2
回到顶部