Golang中方法内部工作原理的深入解析
Golang中方法内部工作原理的深入解析 我了解方法在语义层面的含义, 但想了解其内部机制
- 函数是如何构建的
- 方法是如何构建的
- 两者之间的区别
- 方法和函数的调用机制是如何工作的
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和编译生成的汇编代码,可以验证上述机制。方法本质上是语法糖,底层实现与函数一致,但提供了更自然的面向对象编程方式。

