一尘不染

范围循环中指针和值切片之间的差异

go

请检查以下代码段:

package main

import (  
    "fmt"
    "time"
)

type field struct {  
    name string
}

func (p *field) print() {  
    fmt.Println(p.name)
}

func main() {
    fmt.Println("use values:")

    // use values in range loop and go rountines
    values := []field{{"one"},{"two"},{"three"}}
    for _, v := range values {
        go v.print()
    }

    time.Sleep(time.Second)

    fmt.Println()
    fmt.Println("use pointers:")

    // use pointers in range loop and go rountines
    poniters := []*field{{"one"},{"two"},{"three"}}
    for _, v := range poniters {
        go v.print()
    }

    time.Sleep(time.Second)
}

链接到这里:https :
//play.golang.org/p/cdryPmyWt5

上面的代码将检查for循环中的指针和值之间的差异,而go语句也同时使用。对于代码:

values := []field{{"one"},{"two"},{"three"}}
for _, v := range values {
    go v.print()
}

我们知道,控制台将打印 三三三 的结果,因为循环运行到其最终的够程开始执行,其中写V作为片的最后一个元素之前。但是指针呢?

poniters := []*field{{"one"},{"two"},{"three"}}
for _, v := range poniters {
    go v.print()
}

好像打印 一二三 ,为什么呢?

谢谢。


阅读 181

收藏
2020-07-02

共1个答案

一尘不染

答:在调用函数之前先评估参数。在对它们进行评估之后,调用的参数将按值传递给函数,并且被调用函数开始执行,因此:

第一go v.print()是语法糖go (*field).print(&v)
所述第二go v.print()是语法糖go (*field).print(v)

如果for在goroutines启动之前第一个循环完成,&v则调用相同,这三个调用都相同。通过time.Sleep(100)go v.print()第一个循环之后添加,请参见代码2 。或go func(v field) { v.print() }(v)The Go
Playground(带有的代码3 sync.WaitGroup)上使用

另外,您在这里有 数据竞赛 (请参阅B)。

而对于第二个go (*field).print(v)位置v是之前调用参数评估真实指针和三个够程print,并 有三种不同的地址

1-在The Go Playground上尝试:

package main

import (
    "fmt"
    "time"
)

type field struct {
    name string
}

func (p *field) print() {
    fmt.Println(p.name)
}

func main() {
    fmt.Println("use values:")

    // use values in range loop and go rountines
    values := []field{{"one"}, {"two"}, {"three"}}
    for _, v := range values {
        fmt.Println(&v)
        go (*field).print(&v) //go v.print()
    }

    time.Sleep(time.Second)

    fmt.Println()
    fmt.Println("use pointers:")

    // use pointers in range loop and go rountines
    poniters := []*field{{"one"}, {"two"}, {"three"}}
    for _, v := range poniters {
        fmt.Println(v)
        go (*field).print(v) //go v.print()
    }

    time.Sleep(time.Second)
}

输出:

use values:
&{one}
&{two}
&{three}
three
three
three

use pointers:
&{one}
&{two}
&{three}
two
one
three

2-在Go Playground上尝试:

package main

import (
    "fmt"
    "time"
)

type field struct {
    name string
}

func (p *field) print() {
    fmt.Println(p.name)
}

func main() {
    fmt.Println("use values:")

    // use values in range loop and go rountines
    values := []field{{"one"}, {"two"}, {"three"}}
    for _, v := range values {
        fmt.Println(&v)
        go v.print() //go (*field).print(&v) //
        time.Sleep(100)
    }

    time.Sleep(time.Second)

    fmt.Println()
    fmt.Println("use pointers:")

    // use pointers in range loop and go rountines
    poniters := []*field{{"one"}, {"two"}, {"three"}}
    for _, v := range poniters {
        fmt.Println(v)
        go v.print() //go (*field).print(v) //
    }

    time.Sleep(time.Second)
}

输出:

use values:
&{one}
one
&{two}
two
&{three}
three

use pointers:
&{one}
&{two}
&{three}
one
two
three

B:您之间存在数据争夺,请尝试go build -race
您的代码,然后运行生成的文件WARNING: DATA RACE

输出:

use values:
==================
WARNING: DATA RACE
Read at 0x00c042030210 by goroutine 6:
  runtime.convT2E()
      Go/src/runtime/iface.go:155 +0x0
  main.(*field).print()
      .../m.go:14 +0x6c

Previous write at 0x00c042030210 by main goroutine:
  main.main()
      .../m.go:22 +0x1c3

Goroutine 6 (running) created at:
  main.main()
      .../m.go:23 +0x204
==================
two
three
three

use pointers:
one
two
three
Found 1 data race(s)
2020-07-02