将引用复制到指针或按值传递

huangapple go评论69阅读模式
英文:

Copying reference to pointer or by value

问题

我认为我理解了这里的答案,但以防万一,我想明确询问以下问题(如果你认为这是同一个问题,对不起,但对我来说,它在关注点上感觉不同):

func f() *int {
  d := 6
  pD := new(int)
  pD = &d // 选项1
  *pD = d // 选项2
  return pD
}

第一种选项,即将引用作为指针进行复制,在性能上更优(这是一种教育猜测,但似乎很明显)。我更喜欢这种方法/模式。

第二种选项将进行(浅)复制。我认为这种方法会复制,所以我不担心 GC 会清除实例"d"。由于我作为初学者不太自信(或者说是无知),我经常使用这种方法。

我担心的是,在第一种方法中(传递"d"的地址),GC 是否会识别到它(变量"d")被指针容器引用,因此不会被清除?因此,使用这种方法是安全的吗?也就是说,我可以安全地在应用程序的整个生命周期中传递从函数"f()"返回的指针"pD"吗?

参考:https://play.golang.org/p/JWNf5yRd_B

英文:

I think I understand the answer from here but just in case, I want to explicitly ask about the following (my apologies if you think it is the same question, but to me, it feels different on the concerns):

func f() *int {
  d := 6
  pD := new(int)
  pD = &d // option 1
  *pD = d // option 2
  return pD
}

The first option where I just copy the reference as a pointer is performance-wise, more optimal (this is educational guess, but it seems obvious). I would prefer this method/pattern.

The second option would (shallow) copy (?) instead. What I presume is that this method, because it copies, I have no concerns about GC sweeping the instance of 'd'. I often use this method due to my insecurity (or ignorance as a beginner).

What I am concerned about (or more so, insecure about) is that in the first method (where address of 'd' is transfered), will GC recognize that it (the 'd' variable) is referenced by a pointer container, thus it will not be swept? Thus it will be safe to use this method instead? I.e. can I safely pass around pointer 'pD' returned from func 'f()' for the lifetime of the application?

Reference: https://play.golang.org/p/JWNf5yRd_B

答案1

得分: 10

没有比官方文档更好的查找位置了:

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := File{fd, name, nil, 0}
    return &f
}

请注意,与C语言不同,返回局部变量的地址是完全安全的;变量相关的存储在函数返回后仍然存在。实际上,获取复合字面量的地址每次都会分配一个新的实例,因此我们可以将这两行代码合并。

(来源:《Effective Go》

因此,第一种选项(返回指向局部变量的指针)是绝对安全的,甚至是鼓励的。通过执行逃逸分析,编译器可以确定变量逃逸了其局部作用域,并将其分配到堆上。

英文:

There is no better place to look than the official documentation:

> func NewFile(fd int, name string) *File {
> if fd < 0 {
> return nil
> }
> f := File{fd, name, nil, 0}
> return &f
> }
>
> Note that, unlike in C, it's perfectly OK to return the address
> of a local variable; the storage associated with the variable survives
> after the function returns.
In fact, taking the address of a composite
> literal allocates a fresh instance each time it is evaluated, so we
> can combine these last two lines.

(source: "Effective Go")

So the first option (returning a pointer to a local variable) is absolutely safe and even encouraged. By performing escape analysis the compiler can tell that a variable escapes its local scope and allocates it on the heap instead.

答案2

得分: 3

简而言之:不。

首先:Go语言中没有"引用"的概念。现在就忘掉这个想法,否则你会自讨苦吃的。真的。想着"通过引用"是完全错误的。

其次:性能完全相同。现在就忘掉这种微小的优化。特别是在处理int类型时。只有当你遇到性能问题时才需要优化。直观上认为"传递一个只有8字节的小指针肯定比复制有30甚至100字节的结构体要快得多"。事实并非如此,至少不是那么简单。

第三:只需写一个func f() *int { d := 6; return &d; }。这里不需要进行任何花哨的操作。

第四:选项2会对int进行"深拷贝"。但这可能会让人产生误解,因为int没有"浅拷贝"的概念,所以我不确定你在这里的问题是什么。Go语言没有深拷贝和浅拷贝的概念。如果你复制一个指针值,那么指针值会被复制。还记得第一点吗?Go语言中没有引用。复制一个指针值只是复制了指针值本身,并不会对指向的值产生任何影响,尤其不会进行拷贝。这表明在Go语言中,拷贝并不是"深拷贝"。在讨论Go语言时,不要再提深拷贝和浅拷贝的概念了。(当然,你可以实现函数来对自定义对象进行"深拷贝")

第五:Go语言有一个正常工作的垃圾回收器。你所做的任何操作都不会有任何影响:只要一个对象是活动的,它就不会被回收;一旦可以回收,它就会被回收。你可以传递、返回、复制、传递地址、解引用指针或者任何你喜欢的操作,都没有关系。垃圾回收器正常工作。(除非你故意使用unsafe包来寻找痛苦和错误。)

英文:

In short: No.

First: There are no "references" in Go. Forget about this idea now, otherwise you'll hurt yourself. Really. Thinking about "by reference" is plain wrong.

Second: Performance is totally the same. Forget about this type of nano optimisations now. Especially when dealing with int. If and only if you have a performance problem: Measure, then optimize. It might be intuitively appealing to think "Handing around a tiny pointer of 8 bytes must be much faster than copying structs with 30 or even 100 bytes." It is not, at least it is not that simple.

Third: Just write it a func f() *int { d := 6; return &amp;d; }. There is no need to do any fancy dances here.

Fourth: Option 2 makes a "deep copy" of the int. But this might be misleading as there are no "shallow copies" of an int so I'm unsure if I understand what you are asking here. Go has no notion of deep vs. shallow copy. If you copy a pointer value the pointer value is copied. You remember the first point? There are no references in Go. A pointer value is a value if copied you have a copy of the pointer value. Such a copy does absolutely nothing to the value pointed to, especially it doesn't do a copy. This would hint that copies in Go are not "deep". Forget about deep/shallow copy when talking about Go. (Of course you can implement functions which perform a "deep copy" of your custom objects)

Fifth: Go has a properly working garbage collector. It makes absolutely no difference what you do: While an object is live it won't be collected and once it can be collected it will be. You can pass, return, copy, hand over, take address, dereference pointers or whatever you like, it just does not matter. The GC works properly. (Unless you are deliberately looking for pain and errors by using package unsafe.)

huangapple
  • 本文由 发表于 2017年2月15日 04:07:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/42235270.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定