当前位置: 首页 > 面试题库 >

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

慕容兴贤
2023-03-14
问题内容

请检查以下代码段:

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()
}

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

谢谢。


问题答案:

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

第一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)


 类似资料:
  • 问题内容: 在指令内部创建 隔离作用域 使我们可以将 外部作用域 映射到 内部作用域 。我们已经看到了六种映射到属性的不同方法: = attr &attr @attr = 和 @ 这些作用域映射选项分别做什么? 问题答案: 这可能会造成混淆,但是希望有一个简单的示例可以阐明这一点。首先,让我们将模型绑定与行为分开。 这是一个小提琴,应该有助于将它们联系在一起:http : //jsfiddle.n

  • 我想查询日期范围内的elasticsearch文档。我现在有两个选择,都很适合我。我已经测试了他们两个。1.范围查询2。距离滤波器 因为我现在有一个小数据集,所以无法测试它们的性能。这两者有什么区别?哪一种方法可以更快地检索文档和响应?

  • 问题内容: 我想有一个通用的方法,无论它是作为指针,切片还是数组提供的,都将始终返回结构值。 我对此的处理方式如下: 去游乐场 如您所见,问题出在从a 或。中获取结构。 我如何扩展上面的函数以从数组或切片中获取struct的值? 更新:我要做的就是将变成。 问题答案: 如果你只是想要的类型,即使片是零,你可以使用像这样: 关于,来自http://golang.org/pkg/reflect/#Ty

  • 我使用的是mongoDB PHP,当我使用find查询或聚合框架时,结果是一个游标,我可以使用foreach循环或while循环对游标进行迭代。例如,我要执行以下查询 我可以使用以下两种方法迭代结果

  • 给定max number和range number,我希望打印出以下内容,但尽可能简短。我不知道这是否可以通过IntStream实现。 输入:max=36(或任意数)范围=10(或任意数) 输出:0-910-1920-2930-35 我的代码:

  • 问题内容: 在下一个稳定的AngularJS发行时,我将应用程序从 1.0.8 迁移到 1.2 。 在AngularJS 1.0.8中 ,可以为诸如的指令设置隔离范围。然后,该指令将使用其自己的功能而不是控制器的功能。 Java脚本 在AngularJS 1.2中, 此行为不再起作用。单击该按钮将立即激活控制器的功能。 参见以下jsFiddle比较: 角1.0.8 角1.2.0 到底发生了什么变化