Golang中方法内部工作原理的深入解析

Golang中方法内部工作原理的深入解析 我了解方法在语义层面的含义, 但想了解其内部机制

  1. 函数是如何构建的
  2. 方法是如何构建的
  3. 两者之间的区别
  4. 方法和函数的调用机制是如何工作的
2 回复

以下是指向编译不同阶段函数表示的指针,以及函数调用转换为机器码的部分过程。您可以在代码中看到方法之间的差异。

		NameList []*Name
		Type     Expr // nil means no type
		Values   Expr // nil means no values
		decl
	}

	// func          Name Type { Body }
	// func          Name Type
	// func Receiver Name Type { Body }
	// func Receiver Name Type
	FuncDecl struct {
		Pragma     Pragma
		Recv       *Field // nil means regular function
		Name       *Name
		TParamList []*Field // nil means no type parameters
		Type       *FuncType
		Body       *BlockStmt // nil means no body (forward declaration)
		decl
	}
)
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"fmt"
	"strings"
	"unicode/utf8"
)

// A Func corresponds to a single function in a Go program
// (and vice versa: each function is denoted by exactly one *Func).
//
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssa

import (
	"cmd/compile/internal/abi"
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/src"
	"fmt"
	"math"
	"strings"
)

// A Func represents a Go func declaration (or function literal) and its body.
	"cmd/compile/internal/types"
	"cmd/internal/src"
	"fmt"
)

func postExpandCallsDecompose(f *Func) {
	decomposeUser(f)    // redo user decompose to cleanup after expand calls
	decomposeBuiltIn(f) // handles both regular decomposition and cleanup.
}

func expandCalls(f *Func) {
	// Convert each aggregate arg to a call into "dismantle aggregate, store/pass parts"
	// Convert each aggregate result from a call into "assemble aggregate from parts"
	// Convert each multivalue exit into "dismantle aggregate, store/return parts"
	// Convert incoming aggregate arg into assembly of parts.
	// Feed modified AST to decompose.

	sp, _ := f.spSb()

	x := &expandState{
		f:               f,

更多关于Golang中方法内部工作原理的深入解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang中方法内部工作原理解析

1. 函数是如何构建的

在Go中,函数在编译时被转换为函数值(funcval),包含函数入口地址和闭包上下文。底层实现使用runtime.funcval结构:

// 底层表示(简化)
type funcval struct {
    fn uintptr  // 函数入口地址
    // 可能包含闭包数据
}

// 示例函数
func add(a, b int) int {
    return a + b
}

// 编译后大致等价于
var add_funcval = funcval{
    fn: uintptr(unsafe.Pointer(&add_code)),
}

函数调用时,编译器生成调用指令,传递参数和返回值空间地址。

2. 方法是如何构建的

方法是带有接收者的函数。编译时,方法被转换为普通函数,接收者作为第一个参数:

type Point struct {
    X, Y int
}

// 方法定义
func (p Point) Distance() float64 {
    return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))
}

// 编译后转换为
func Point_Distance(p Point) float64 {
    return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))
}

对于指针接收者:

func (p *Point) Scale(factor int) {
    p.X *= factor
    p.Y *= factor
}

// 编译后转换为
func Point_Scale(p *Point, factor int) {
    p.X *= factor
    p.Y *= factor
}

3. 函数与方法的区别

语法层面:

// 函数
func Add(a, b int) int { return a + b }

// 方法
func (p Point) Add(q Point) Point {
    return Point{p.X + q.X, p.Y + q.Y}
}

底层实现相同:

// 方法调用
p := Point{3, 4}
result := p.Add(Point{1, 2})

// 等价于函数调用
result = Point_Add(p, Point{1, 2})

方法集规则:

  • 类型T的方法集包含所有接收者为T的方法
  • 类型T的方法集包含所有接收者为T或T的方法

4. 调用机制详解

静态调用(直接调用):

func directCall() {
    p := Point{3, 4}
    // 编译器直接解析方法地址
    d := p.Distance()
    _ = d
}

动态调用(通过接口):

type Shaper interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func interfaceCall() {
    var s Shaper = Rectangle{3, 4}
    // 通过接口方法表查找
    area := s.Area()
    _ = area
}

方法值(Method Value):

func methodValue() {
    p := Point{3, 4}
    // 创建方法值,绑定接收者
    distFunc := p.Distance
    // 调用时无需再传递接收者
    d := distFunc()
    _ = d
}

方法表达式(Method Expression):

func methodExpression() {
    // 将方法作为函数使用
    distFunc := Point.Distance
    p := Point{3, 4}
    // 显式传递接收者作为第一个参数
    d := distFunc(p)
    _ = d
}

底层接口方法调用:

// 接口底层表示
type iface struct {
    tab  *itab          // 类型和方法表信息
    data unsafe.Pointer // 实际数据指针
}

// 方法调用大致转换为
// s.Area() => s.tab.fun[0](s.data)

性能考虑:

  • 直接方法调用:编译期确定,无额外开销
  • 接口方法调用:运行时查找方法表,有微小开销
  • 方法值:创建时捕获接收者,调用时无接收者查找开销

通过分析Go源码中的runtime/iface.go和编译生成的汇编代码,可以验证上述机制。方法本质上是语法糖,底层实现与函数一致,但提供了更自然的面向对象编程方式。

回到顶部