返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

6.3 方法值

发布于 2024-10-12 19:15:48 字数 2893 浏览 0 评论 0 收藏 0

和函数一样,方法除直接调用外,还可赋值给变量,或作为参数传递。

依照引用方式不同,分为表达式(expression)和 值(value) 两种。

type N int

func (n N) copy() {
	fmt.Printf("%p, %v\n", &n, n)
}

func (n *N) ref() {
	fmt.Printf("%p, %v\n", n, *n)
}

// -----------------------------

func main() {
	var n N = 100

	// expression
	var e func(*N)() = (*N).ref
	e(&n)

	// value
	var v func() = n.ref
	v()
}

// 0xc000014080, 100
// 0xc000014080, 100

表达式(expr)很好理解,将方法还原为普通函数,显式传递接收参数(receiver)。

而方法值(value)似乎打包了接收参数和方法,导致签名有所不同。

精简代码,看看具体如何实现。

type N int

func (n N) copy() {
	println(n)
}

func (n *N) ref() {
	println(*n)
}

// -----------------------------

func test(f func()) {
	f()
}

func main() {
	var n N = 100
	var v func() = n.copy

	n++
	n.copy() // 101

	v()      // 100
	test(v)  // 100
}
$ go build -gcflags "-N -l"
$ go tool objdump -S -s "main\.main" ./test

func main() {

    var n N = 100
    0x4552b4     MOVQ $0x64, 0x8(SP)   
    
    var v func() = n.copy
    0x4552c3     LEAQ 0x10(SP), CX               
    0x4552cf     LEAQ N.copy-fm(SB), DX     
    0x4552d6     MOVQ DX, 0x10(SP)               
    0x4552dd     MOVQ 0x8(SP), DX         0x0 +-------------+
    0x4552e2     MOVQ DX, 0x18(SP)            |             |
    0x4552e7     MOVQ CX, 0x20(SP)        0x8 +-------------+
                                              | n = 100     |
    n++                                  0x10 +-------------+     
    0x4552ec     MOVQ 0x8(SP), CX             | copy-fm     |
    0x4552f1     LEAQ 0x1(CX), AX        0x18 +-------------+     
    0x4552f5     MOVQ AX, 0x8(SP)             | 100         |
                                         0x20 +-------------+
    n.copy() // 101                           | ptr -> 0x10 |    
    0x4552fa     CALL N.copy(SB)              +-------------+
    
    v()      // 100
    0x4552ff     MOVQ 0x20(SP), DX
    0x455304     MOVQ 0(DX), CX
    0x455307     CALL CX          ; copy-fm
    
    test(v)  // 100
    0x455309     MOVQ 0x20(SP), AX       
    0x45530e     CALL test(SB)      
}
  • 方法值: funcval { method-fm, receiver-copy }
  • 换成 n.ref ,无非是复制 *N 而已。
TEXT main.N.copy-fm(SB) <autogenerated>

    0x45535d     MOVQ 0x8(DX), AX      // receiver
    0x455361     MOVQ AX, 0x8(SP)
    0x455366     CALL N.copy(SB)


TEXT main.test(SB)

    0x455277     MOVQ 0(AX), CX       // N.copy-fm
    0x45527a     MOVQ AX, DX          // DX
    0x45527d     CALL CX              

对于空指针(nil),注意内存安全。

type N int
func (n N) copy() {}
func (n *N) ref() {}

// -----------------------------

func main() {
	var p *N

	p.ref()         // value
	(*N)(nil).ref() // value
	(*N).ref(nil)   // expression
    
	// p.copy()
	// ~~~~~~ invalid memory address or nil pointer dereference
    //           N.copy(*p)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文