当我看到如下代码时,我有点困惑:
bigBox := &BigBox{}
bigBox.BubbleGumsCount = 4 // correct...
bigBox.SmallBox.AnyMagicItem = true // also correct
为什么或何时,我想要bigBox := &BigBox{}
代替bigBox := BigBox{}
?它在某种程度上更有效吗?
代码示例取自here。
样品2:
package main
import "fmt"
type Ints struct {
x int
y int
}
func build_struct() Ints {
return Ints{0,0}
}
func build_pstruct() *Ints {
return &Ints{0,0}
}
func main() {
fmt.Println(build_struct())
fmt.Println(build_pstruct())
}
样品编号。 3 :(为什么我会在这个例子中使用& BigBox,而不是直接使用BigBox作为结构?)
func main() {
bigBox := &BigBox{}
bigBox.BubbleGumsCount = 4
fmt.Println(bigBox.BubbleGumsCount)
}
是否有理由调用build_pstruct而不是build_struct变体?这不是我们拥有GC的原因吗?
答案 0 :(得分:5)
我找到了这种代码的一个动机:避免“偶然复制结构”。
如果使用struct变量来保存新创建的结构:
bigBox := BigBox{}
你可以像这样意外复制结构
myBox := bigBox // Where you just want a refence of bigBox.
myBox.BubbleGumsCount = 4
或者像这样
changeBoxColorToRed(bigBox)
其中changeBoxColorToRed
是
// It makes a copy of entire struct as parameter.
func changeBoxColorToRed(box bigBox){
// !!!! This function is buggy. It won't work as expected !!!
// Please see the fix at the end.
box.Color=red
}
但是如果使用结构指针:
bigBox := &BigBox{}
中没有复制
myBox := bigBox
和
changeBoxColorToRed(bigBox)
将无法编译,让您有机会重新考虑changeBoxColorToRed
的设计。修复很明显:
func changeBoxColorToRed(box *bigBox){
box.Color=red
}
changeBoxColorToRed
的新版本不会复制整个结构并且可以正常工作。
答案 1 :(得分:3)
bb := &BigBox{}
创建一个结构,但将变量设置为指向它的指针。它与bb := new(BigBox)
相同。另一方面,bb := BigBox{}
使bb成为BigBox类型的变量。如果你想要一个指针(因为可能是因为你要通过指针使用数据),那么最好将bb作为指针,否则你将会编写很多&bb
。如果您要将数据直接用作结构,那么您希望bb成为结构体,否则您将使用*bb
取消引用。
这不是问题的关键,但通常最好一次创建数据,而不是通过创建对象并随后更新它来递增。
bb := &BigBox{
BubbleGumsCount: 4,
SmallBox: {
AnyMagicItem: true,
},
}
答案 2 :(得分:2)
&
获取某个地址。所以它意味着"我想要指向"而不是"我想要一个"的实例。包含值的变量的大小取决于值的大小,可以是大小。包含指针的变量的大小为8个字节。
以下是示例及其含义:
bigBox0 := &BigBox{} // bigBox0 is a pointer to an instance of BigBox{}
bigBox1 := BigBox{} // bigBox1 contains an instance of BigBox{}
bigBox2 := bigBox // bigBox2 is a copy of bigBox
bigBox3 := &bigBox // bigBox3 is a pointer to bigBox
bigBox4 := *bigBox3 // bigBox4 is a copy of bigBox, dereferenced from bigBox3 (a pointer)
为什么要指针?
为什么不使用指针?
我的一般建议是谨慎使用指针。除非您处理需要传递的非常大的对象,否则在堆栈上传递副本通常会更好。减少垃圾是一件大事。垃圾收集器会变得更好,但是通过尽可能降低垃圾收集器会让你感觉更好。
答案 3 :(得分:1)
区别在于创建参考对象(使用&符号)与值对象(不使用&符号)。
对于传递价值与参考类型的一般概念有一个很好的解释...... What's the difference between passing by reference vs. passing by value?
关于Go here ... http://www.goinggo.net/2013/07/understanding-pointers-and-memory.html
,对这些概念进行了一些讨论答案 4 :(得分:1)
一般情况下,&BigBox{}
和BigBox{}
之间没有区别。只要语义正确,Go编译器就可以自由地做任何事情。
func StructToStruct() {
s := Foo{}
StructFunction(&s)
}
func PointerToStruct() {
p := &Foo{}
StructFunction(p)
}
func StructToPointer() {
s := Foo{}
PointerFunction(&s)
}
func PointerToPointer() {
p := &Foo{}
PointerFunction(p)
}
//passed as a pointer, but used as struct
func StructFunction(f *Foo) {
fmt.Println(*f)
}
func PointerFunction(f *Foo) {
fmt.Println(f)
}
大会摘要:
StructToStruct
:13行,无分配PointerToStruct
:16行,无分配StructToPointer
:20行,分配堆PointerToPointer
:12行,堆分配使用完美的编译器,*ToStruct
函数与*ToPointer
函数的函数相同。 Go的转义分析足以判断指针是否在模块边界内转义。哪种方式最有效的是编译器的方式。
如果您真的想进入微优化,请注意,当语法与语义对齐时,Go是最有效的(结构用作结构,指针用作指针)。或者你可以忘记它,并按照它的使用方式声明变量,大部分时间你都是正确的。
注意:如果Foo
真的很大PointerToStruct
将堆分配它。该规范威胁说,即使StructToStruct
被允许这样做,但我无法实现。这里的教训是编译器会做任何想做的事情。正如寄存器的细节与代码屏蔽一样,堆/堆栈的状态也是如此。不要更改代码,因为您认为您知道编译器将如何使用堆。