Go空结构体传引用?

443 阅读2分钟

我们看个空结构体例子:

package main

import "fmt"

type User struct{
}

func test(u User){
    fmt.Printf("test: %p\n",&u)
}

func main() {
	user:=User{}
	test(user)
	fmt.Printf("main-user: %p\n",&user)
}

结果:

test: 0x5a6d68 

main-user: 0x5a6d68 

我们再看一下不是空结构体是什么结果:

package main

import "fmt"

type User struct{
	age int
}

func test(u User){
	fmt.Printf("test: %p\n",&u)
}

func main() {
	user:=User{age:90}
	test(user)
	fmt.Printf("main-user: %p\n",&user)
} 

结果:

test: 0xc00000a0c8 

main-user: 0xc00000a0c0 

我们发现空结构体地址是一样的,非空结构体地址不一样,Go语言不是只有值传递嘛,为什么第一种不是这样的情况呢?

感兴趣的伙伴可以执行go tool compile -N -l -S main.go,可以得到汇编部分

通过runtime.newobject(SB)分配内存:

func newobject(typ *_type) unsafe.Pointer {
	return mallocgc(typ.size, typ, true)
}

newobject()中主要是调用了mallocgc()方法,在这里我找到了答案。因为mallocgc()代码比较长

如果 size0 的时候,统一返回的都是全局变量 zerobase 的地址。【PS:正常struct是占用一小块内存的,并且结构体的大小是要经过边界,长度的对齐的,但是“空结构体”是不占内存的,size为0】。总结原因:

因为空结构体是不占用内存的,所以size为0,在内存分配时,size0会统一返回zerobase的地址,所以空结构体在进行参数传递时,发生值拷贝后地址都是一样的,才造成了这个质疑Go不是值传递的假象。

也就是说如果我定义了两个空结构体,那么他们的地址应该是一样的,我们来验证一下:

package main

import (
	"fmt"
)

type User struct {
}

type Dog struct {
}

func main() {
	user := User{}
	dog := Dog{}
	fmt.Printf("main-user: %p\n", &user)
	fmt.Printf("main-dog: %p\n", &dog)
}

结果:

main-user: 0x5a6d68 

main-dog: 0x5a6d68 

结果证明果然是一样的。